Klant: Useq

Project: Spatial Organ Atlas

Dataset: Kidney

date: 12 56 Tue 25 April, 2023

loading dependencies Please make sure the following packages are installed and required libraries can be loaded:

  • install.packages(“pkgbuild”) // pkgbuild::check_build_tools()
  • install.packages(“devtools”)
  • devtools::install_github(“Nanostring-Biostats/NanoStringNCTools”)
  • devtools::install_github(“Nanostring-Biostats/GeomxTools”, ref = “dev”)
  • BiocManager::install(“GeoMxWorkflows”)
  • devtools::install_github(“DavisLaboratory/standR”)
  • BiocManager::install(“SpatialDecon”)
  • BiocManager::install(“GSVA”)
  • install.packages(“plotly”)
  • install.packages(“DT”)
  • install.packages(“msigdbr”)
  • install.packages(“digest”)
  • install.packages(“rmarkdown”)
  • install.packages(“kable”)
  • BiocManager::install(“EBImage”)
  • install.packages(‘scattermore’)
  • install.packages(‘pbapply’)
  • install.packages(‘plotrix’)
  • install.packages(‘ggtext’)
  • install.packages(‘RBioFormats’)
  • BiocManager::install(“aoles/RBioFormats”)
  • install.packages(“C:/Users/inijman/Downloads/SpatialOmicsOverlay-0.99.13-beta.tar.gz”, dependencies = TRUE, repos = NULL)
  • devtools::install_github(“DavisLaboratory/standR”)
  • BiocManager::install(“clusterProfiler”)
  • BiocManager::install(“pathview”)
#load libraries

#stack_size <- getOption("pandoc.stack.size", default = "512m")
#options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m, -XX:MetaspaceSize=1024M"))
options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m"))
library(NanoStringNCTools)
library(GeomxTools)
library(GeoMxWorkflows)
library(SpatialDecon)
library(GSVA) #for pathway analyses
library(msigdbr) #for molecular signatures in pathway analyses
library(knitr)
library(dplyr)
library(ggforce)
library(ggplot2)
library(scales) # for percent
library(reshape2)  # for melt
library(cowplot)   # for plot_grid
library(umap)
library(Rtsne)
library(pheatmap)  # for pheatmap
library(ggrepel) 
library(scales) #for ggplot peaudolog to prevent errors on log(0)
library(DT)
library(plotly)
library(gridExtra)
library(RColorBrewer)
library(SpatialOmicsOverlay)
library(gt)
library(tidyverse)
library(clusterProfiler)
library(fgsea)
library(pathview)
library(png)
library(readxl)
library(viridis)

library(standR)
library(SpatialExperiment)
library(limma)
library(ggalluvial)
library(scater)

BiocManager::install("sva")
library(sva)

#BiocManager::install("preprocessCore") #quantile norm
library(preprocessCore)

#install.packages("Polychrome") #Get colors
#library(Polychrome)


library(infercnv) # CNV
library("ape")
library("Biostrings")
library("ggtree")
library(tidytree)
library(ggpubr)

1 loading base files

# Reference the main folder 'file.path' containing the sub-folders with each data file type:
datadir<-file.path("L:/pkloosterman/Github/DKD_Kidney/")

To locate a specific file path replace the above line with datadir <- file.path(“~/Folder/SubFolder/DataLocation”) replace the Folder, SubFolder, DataLocation as needed. The DataLocation folder should contain a dccs, pkcs, and annotation folder with each set of files present as needed automatically list files in each directory for use.

Take care you import a column with nuclei count separately if you want.

DCCFiles <- dir(file.path(datadir, "dccs"), pattern = ".dcc$",
                full.names = TRUE, recursive = TRUE)
PKCFiles <- dir(file.path(datadir, "pkcs"), pattern = ".pkc$",
                full.names = TRUE, recursive = TRUE)
SampleAnnotationFile <-
  dir(file.path(datadir, "annotation"), pattern = "^[^~]",
      full.names = TRUE, recursive = TRUE)

2 load data

Data <-
  readNanoStringGeoMxSet(dccFiles = DCCFiles,
                         pkcFiles = PKCFiles,
                         phenoDataFile = SampleAnnotationFile,
                         phenoDataSheet = "Template",
                         phenoDataDccColName = "Sample_ID",
                         protocolDataColNames = c("aoi", "roi"),
                         experimentDataColNames = c("panel"))

#save data to prevent loading time for retakes
#saveData<-Data
#Data<-saveData

#change Data column names and manual correction of spelling errors
Data@phenoData@data[["slide_name"]]<-Data@phenoData@data[["slide name"]]
Data@phenoData@data[["slide name"]]<-  NULL


#+1 references the slide name column
ann_size<-length(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))])+1 
ann_names<-c(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))],"slide_name")

# Feel free to change the order of which colors are appointed.
color<-c("#A349A4", "#FFFF33", "#E7298A", "#091833", "#1B9E77", "#D95F02", "#7570B3",  "#66A61E", "#E6AB02", "#8DD3C7", "#9F000F", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F", "#377EB8", "#984EA3", "#4DAF4A", "#FF71CE", "#FF7F00", "#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#CAB2D6", "#6A3D9A", "#FFFF99", "#B15928")

# Use count and count_max to set the range of color needed for each column.
# With setNames() the color code is linked to each unique individual value of each column.
count=1
color_list = list()
for (ann in ann_names) {
  count_max = count+length(unique(Data@phenoData@data[[ann]]))-1
  color_list[[ann]]<-setNames(color[count:count_max], unique(Data@phenoData@data[[ann]]))
  count=count_max+1
}
value_names = c()
for (ann in ann_names) {
  value_names <- c(value_names, unique(Data@phenoData@data[[ann]]))
}

color_df <- as.data.frame(value_names)
color_df$color <- color[0:length(value_names)]

color_df %>% 
  mutate(color = fct_inorder(color)) |> 
  gt() %>% 
  data_color(columns = color, colors = as.character(color)) %>%
  tab_options(container.height = 500)
value_names color
DKD #A349A4
normal #FFFF33
disease3 #E7298A
disease4 #091833
normal3 #1B9E77
normal4 #D95F02
disease1B #7570B3
disease2B #66A61E
normal2B #E6AB02
glomerulus #8DD3C7
tubule #9F000F
abnormal #BEBADA
NA #FB8072
normal #80B1D3
niormal #FDB462
disease3 #B3DE69
disease4 #FCCDE5
normal3 #D9D9D9
normal4 #BC80BD
disease1B #CCEBC5
disease2B #FFED6F
normal2B #377EB8
paste("Reads from following runs used: ",unique(pData(protocolData(Data))$SeqSetId))
## [1] "Reads from following runs used:  "

3 Study design

pkcs <- annotation(Data)
modules <- gsub(".pkc", "", pkcs)
kable(data.frame(PKCs = pkcs, modules = modules))
PKCs modules
TAP_H_WTA_v1.0.pkc TAP_H_WTA_v1.0

Select the annotations we want to show, use `` to surround column names with spaces or special symbols

#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,slide_name)
count_mat <- pData(Data) %>% dplyr::count(pData(Data)[c(ann_names)])

Simplify the slide names if required

# count_mat$slide_name <- gsub("disease", "d", gsub("normal", "n", count_mat$slide_name))
#count_mat$path_ann <- gsub("i", "", count_mat$path_ann) #correcting spelling error

Gather the data and plot in order: class, slide name, region, segment

test_gr <- gather_set_data(count_mat, 1:all_of(ann_size))
test_gr$x <- factor(test_gr$x, labels = ann_names)

Plot Sankey

ggplot(test_gr, height = 10, width = 10, aes(x, id = id, split = y, value = n)) +
  geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.2) +
  geom_parallel_sets_labels(color = "white", size = 5) +
  theme_classic(base_size = 12) + 
  theme(legend.position = "bottom",
        axis.ticks.y = element_blank(),
        axis.line = element_blank(),
        axis.text.y = element_blank()) +
  scale_y_continuous(expand = expansion(0)) + 
  scale_x_discrete(expand = expansion(0)) +
  labs(x = "", y = "") +
  scale_fill_manual(values=color_list$ANN2) +
  annotate(geom = "segment", x = 4.25, xend = 4.25,
           y = 10, yend = 61, lwd = 2) +
  annotate(geom = "text", x = 4.19, y = 25, angle = 90, size = 5,
           hjust = 0.5, label = "50 segments")

4 QC & Pre-processing

Shift counts to one

#shift any expression counts with a value of 0 to 1 to enable in downstream transformations.
Data <- shiftCountsOne(Data, useDALogic = TRUE)

4.1 Segment QC

We first assess sequencing quality and adequate tissue sampling for every ROI/AOI segment.

Every ROI/AOI segment will be tested for:

Raw sequencing reads: segments with >1000 raw reads are removed. % Aligned,% Trimmed, or % Stitched sequencing reads: segments below ~80% for one or more of these QC parameters are removed. % Sequencing saturation ([1-deduplicated reads/aligned reads]%): segments below ~50% require additional sequencing to capture full sample diversity and are not typically analyzed until improved. Negative Count: this is the geometric mean of the several unique negative probes in the GeoMx panel that do not target mRNA and establish the background count level per segment; segments with low negative counts (1-10) are not necessarily removed but may be studied closer for low endogenous gene signal and/or insufficient tissue sampling. No Template Control (NTC) count: values >1,000 could indicate contamination for the segments associated with this NTC; however, in cases where the NTC count is between 1,000- 10,000, the segments may be used if the NTC data is uniformly low (e.g. 0-2 counts for all probes). Nuclei: >100 nuclei per segment is generally recommended; however, this cutoff is highly study/tissue dependent and may need to be reduced; what is most important is consistency in the nuclei distribution for segments within the study. Area: generally correlates with nuclei; a strict cutoff is not generally applied based on area.

4.1.1 Select Segment QC

First, we select the QC parameter cutoffs, against which our ROI/AOI segments will be tested and flagged appropriately. We have selected the appropriate study-specific parameters for this study. Note: the default QC values recommended above are advised when surveying a new dataset for the first time.

Default QC cutoffs are commented in () adjacent to the respective parameters study-specific values were selected after visualizing the QC results in more detail below

QC_params <-
  list(minSegmentReads = 1000, # Minimum number of reads (1000)
       percentTrimmed = 80,    # Minimum % of reads trimmed (80%)
       percentStitched = 80,   # Minimum % of reads stitched (80%)
       percentAligned = 75,    # Minimum % of reads aligned (80%)
       percentSaturation = 50, # Minimum sequencing saturation (50%)
       minNegativeCount = 1,   # Minimum negative control counts (10)
       maxNTCCount = 9000,     # Maximum counts observed in NTC well (1000)
       minNuclei = 20,        # Minimum # of nuclei estimated (100)
       minArea = 1000)         # Minimum segment area (5000)
Data <-
  setSegmentQCFlags(Data, qcCutoffs = QC_params)        

cat("pre-QC features:", dim(Data)[1], "\npre-QC samples:", dim(Data)[2])
## pre-QC features: 18642 
## pre-QC samples: 276
#Table for clarification
QCparams_df <- data.frame (
  items = c("minSegmentReads","percentTrimmed","percentStitched","percentAligned","percentSaturation",
            "minNegativeCount","maxNTCCount","minNuclei","minArea"),
  defaults = c(1000,80,80,80,50,10,1000,100,5000),
  actual = c(QC_params$minSegmentReads,QC_params$percentTrimmed,QC_params$percentStitched,QC_params$percentAligned,QC_params$percentSaturation,QC_params$minNegativeCount,QC_params$maxNTCCount,QC_params$minNuclei,QC_params$minArea)
)

datatable(QCparams_df, rownames=FALSE,
          caption = "QC thresholds",
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)

Collate QC Results

QCResults <- protocolData(Data)[["QCFlags"]]
flag_columns <- colnames(QCResults)
QC_Summary <- data.frame(Pass = colSums(!QCResults[, flag_columns]),
                         Warning = colSums(QCResults[, flag_columns]))

QCResults$QCStatus <- apply(QCResults, 1L, function(x) {
  ifelse(sum(x) == 0L, "PASS", "WARNING")
})

QC_Summary["TOTAL FLAGS", ] <-
  c(sum(QCResults[, "QCStatus"] == "PASS"),
    sum(QCResults[, "QCStatus"] == "WARNING"))

col_by <- "ANN1"
col_by_plate <- "Plate_ID"

4.2 Graphical summaries of QC statistics

Use the tab-menu to navigate!

QC_histogram <- function(assay_data = NULL,
                         annotation = NULL,
                         fill_by = NULL,
                         thr = NULL,
                         scale_trans = NULL) {
  plt <- ggplot(assay_data,
                aes_string(x = paste0("unlist(`", annotation, "`)"),
                           fill = fill_by)) +
    geom_histogram(bins = 50) +
    geom_vline(xintercept = thr, lty = "dashed", color = "black") +
    theme_bw() + guides(fill = "none") +
    facet_wrap(as.formula(paste("~", fill_by)), nrow = 4) +
    scale_fill_manual(values=color_list$ANN1) +
    labs(x = annotation, y = "segments, #", title = annotation)
  if(!is.null(scale_trans)) {
    plt <- plt +
      scale_x_continuous(trans = scale_trans)
  }
  plt
}

Trimmed

QC_histogram(sData(Data), "Trimmed (%)", col_by, QC_params$percentTrimmed)

Stiched (%)

QC_histogram(sData(Data), "Stitched (%)", col_by, QC_params$percentStitched)

Aligned (%)

QC_histogram(sData(Data), "Aligned (%)", col_by,QC_params$percentAligned)

Sequencing Saturation (%)

QC_histogram(sData(Data), "Saturated (%)", col_by, QC_params$percentSaturation) +
  labs(title = "Sequencing Saturation (%)",
       x = "Sequencing Saturation (%)")

Area

QC_histogram(sData(Data), "area", col_by, QC_params$minArea, scale_trans = "log10")

Nuclei count

QC_histogram(sData(Data), "nuclei", col_by, QC_params$minNuclei)

DuplicationRate

ggplot(pData(protocolData(Data)),
       aes(x = Plate_ID, fill=Plate_ID,
          y = (DeduplicatedReads/Raw))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Deduplicated / Raw reads") +
  scale_y_continuous(labels = scales::percent) +
  theme_bw()

Negprobes vs Endogenous

tmp_target_Data <- aggregateCounts(Data)

#get negative probe data
negs<-subset(tmp_target_Data,CodeClass=="Negative")

p1<-ggplot(pData(negs),
       aes(x = ANN2, fill = ANN2,
          y = assayDataElement(negs, elt = "exprs"))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Negative probes Expression") +
  scale_y_continuous(limits = c(1,3000), trans = "log2") +
  theme_bw() + coord_flip()


# get endogenous probe data
end<-subset(tmp_target_Data,CodeClass=="Endogenous")

p2<-ggplot(pData(end),
       aes(x = ANN2, fill = ANN2,
           y = colMeans(assayDataElement(end, elt = "exprs")))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Endogenous probes Expression (mean)") +
  scale_y_continuous(limits = c(1,3000),trans = "log2") +
  theme_bw() + coord_flip()

pl <-list(p1,p2)
plot_grid(plotlist=pl, nrow=2, align='v')

Neg_probe reads compared to raw_reads

# make background total neg probe count
fdata_df<-fData(Data)
negprobesnames<-rownames(fdata_df[fdata_df$Negative==TRUE,])
temp_exp<-assayDataElement(Data,elt='exprs')
negprobe_expr_fd<-temp_exp[rownames(temp_exp) %in% negprobesnames,]
tot_neg_ctrl_reads<-colSums(negprobe_expr_fd)
tot_dedup_reads<-pData(protocolData(Data))$DeduplicatedReads

df<-data.frame('aoi'= names(tot_neg_ctrl_reads),'tot_dedup_reads' = as.numeric(tot_dedup_reads),'tot_neg_ctrl_reads'=as.numeric(tot_neg_ctrl_reads))
df<-melt(df,id="aoi")
ggplot(df,aes(fill=variable,y=value,x=aoi)) + 
  geom_bar(position="identity",stat="identity") +
  scale_y_continuous(trans = log2_trans()) +
  theme(legend.position="bottom",axis.text.x = element_blank(),axis.ticks.x=element_blank())                     

Duplicated reads vs Background

# get dcc per plate. sum negprobe counts/dcc/plate
ggplot(pData(protocolData(Data)),
       aes(x = Plate_ID, fill=Plate_ID,
          y = DeduplicatedReads)) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Deduplicated / Raw reads") +
  scale_y_log10()+
  geom_hline(data =pData(protocolData(Data)) , 
           aes(yintercept = NTC, colour=Plate_ID)) +
  theme_bw()

Duplicated reads vs ROIarea

temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))

ggplot(temp_df,
       aes(x = dcc, colour=slide_name,
          y = (DeduplicatedReads/area) )) +
  geom_point() +
  ylim(0,20) + 
  labs(y = "Deduplicated reads / ROI area") +
  theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

Duplicated reads vs nuclei

temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))

ggplot(temp_df,
       aes(x = dcc, colour=slide_name,
          y = (DeduplicatedReads/nuclei) )) +
  geom_point() +
  ylim(0,50) +                                                      # Adjust per project
  labs(y = "Deduplicated reads / nuclei") +
  theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

4.3 Process Negative GeoMeans

# Calculate the negative geometric means for each module
# It will show only the negative probes geomean, so expect less segments.
negativeGeoMeans <- 
  esBy(negativeControlSubset(Data), 
       GROUP = "Module", 
       FUN = function(x) { 
         assayDataApply(x, MARGIN = 2, FUN = ngeoMean, elt = "exprs") 
       }) 
protocolData(Data)[["NegGeoMean"]] <- negativeGeoMeans

negCols <- paste0("NegGeoMean_", modules)
pData(Data)[, negCols] <- sData(Data)[["NegGeoMean"]]
for(ann in negCols) {
  plt <- QC_histogram(pData(Data), ann, col_by, 2, scale_trans = "log10")
  print(plt)
}

# Detatch neg_geomean columns ahead of aggregateCounts call

pData(Data) <- pData(Data)[, !colnames(pData(Data)) %in% negCols]

Show all NTC values, Freq = # of Segments with a given NTC count:

QC<-sData(Data)

ntc_df <-QC[,c("slide_name","Plate_ID","NTC_ID","NTC")]
temptable<-ntc_df %>% dplyr::count(ntc_df$slide_name,ntc_df$NTC_ID,ntc_df$Plate_ID,ntc_df$NTC)
colnames(temptable) <- c("Slide_name","NTC_ID","Plate_ID","NTC_count","Number_of_samples")
datatable(temptable, rownames = FALSE)
kable(table(NTC_Count = sData(Data)$NTC), col.names = c("NTC Count", "# of Segments"))
NTC Count # of Segments
3 64
113 71
397 47
8704 94
kable(QC_Summary, caption = "QC Summary Table for each Segment")
QC Summary Table for each Segment
Pass Warning
LowReads 272 4
LowTrimmed 276 0
LowStitched 273 3
LowAligned 266 10
LowSaturation 272 4
LowNegatives 276 0
HighNTC 276 0
LowNuclei 276 0
LowArea 265 11
TOTAL FLAGS 259 17
datatable(QC_Summary,
          caption = "AOI QC Summary",
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)

Show AOIs which fail critical QCs.

QC<-sData(Data)
undersat<-subset(QC, `Saturated (%)`<= QC_params$percentSaturation)

if(nrow(undersat)> 0) {

datatable(aggregate(undersat, by=list(undersat$SampleID),paste,collapse=";"),
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)}

Subsetting our dataset has removed samples which did not pass QC

Data <- Data[, QCResults$QCStatus == "PASS"]

Generally keep the qcCutoffs parameters unchanged. Set removeLocalOutliers to FALSE if you do not want to remove local outliers

Data <- setBioProbeQCFlags(Data, 
                               qcCutoffs = list(minProbeRatio = 0.1,
                                                percentFailGrubbs = 20), 
                               removeLocalOutliers = FALSE)

ProbeQCResults <- fData(Data)[["QCFlags"]]

Define QC table for Probe QC

qc_df <- data.frame(Passed = sum(rowSums(ProbeQCResults[, -1]) == 0),
                    Global = sum(ProbeQCResults$GlobalGrubbsOutlier),
                    Local = sum(rowSums(ProbeQCResults[, -2:-1]) > 0
                                & !ProbeQCResults$GlobalGrubbsOutlier))

Subset object to exclude all that did not pass Ratio & Global testing

ProbeQCPassed <- 
  subset(Data, 
         fData(Data)[["QCFlags"]][,c("LowProbeRatio")] == FALSE &
           fData(Data)[["QCFlags"]][,c("GlobalGrubbsOutlier")] == FALSE)

Data <- ProbeQCPassed 
cat("After QC features:", dim(Data)[1], "\nAfter QC samples:", dim(Data)[2])
## After QC features: 18641 
## After QC samples: 259

Check how many unique targets the object has

length(unique(featureData(Data)[["TargetName"]]))
## [1] 18504

Collapse to targets

target_Data <- aggregateCounts(Data)

exprs(target_Data)[1:5, 1:2]
##       DSP-1001250007851-H-A02.dcc DSP-1001250007851-H-A03.dcc
## A2M                           485                         262
## NAT2                           15                          18
## ACADM                          31                          15
## ACADS                          27                          17
## ACAT1                          29                          24

Define LOQ SD threshold and minimum value

cutoff <- 2
minLOQ <- 2

4.4 Limit of Quantification

We define a limit of quantification (LOQ) per ROI/AOI segment based on the negative control probes to guide the filtering of segments and genes with low signal relative to background. The formula for calculating the LOQ in the \(i^{th}\) segment at \(n\) standard deviations (\(n = 2\) for this study) is: \(LOQ_i=geomean(NegProbe_i)*geoSD(NegProbe_i)^n\)

Calculate LOQ per module tested

LOQ <- data.frame(row.names = colnames(target_Data))
for(module in modules) {
  vars <- paste0(c("NegGeoMean_", "NegGeoSD_"),
                 module)
  if(all(vars[1:2] %in% colnames(pData(target_Data)))) {
    LOQ[, module] <-
      pmax(minLOQ,
           pData(target_Data)[, vars[1]] * 
             pData(target_Data)[, vars[2]] ^ cutoff)
  }
}
pData(target_Data)$LOQ <- LOQ

4.5 Filtering

After determining the limit of quantification (LOQ) per segment, we recommend filtering out either segments and/or genes with abnormally low signal. Filtering is an important step to focus on the true biological data of interest.

We determine the number of genes detected in each segment across the dataset.

LOQ_Mat <- c()
for(module in modules) {
  ind <- fData(target_Data)$Module == module
  Mat_i <- t(esApply(target_Data[ind, ], MARGIN = 1,
                     FUN = function(x) {
                       x > LOQ[, module]
                     }))
  LOQ_Mat <- rbind(LOQ_Mat, Mat_i)
}
# ensure ordering since this is stored outside of the geomxSet
LOQ_Mat <- LOQ_Mat[fData(target_Data)$TargetName, ]

4.5.1 Segment Gene Detection

We first filter out segments with exceptionally low signal. These segments will have a small fraction of panel genes detected above the LOQ relative to the other segments in the study. Let’s visualize the distribution of segments with respect to their % genes detected:

Save detection rate information to pheno data

pData(target_Data)$GenesDetected <- 
  colSums(LOQ_Mat, na.rm = TRUE)
pData(target_Data)$GeneDetectionRate <-
  pData(target_Data)$GenesDetected / nrow(target_Data)

Determine detection thresholds: 1%, 5%, 10%, 15%, >15%

pData(target_Data)$DetectionThreshold <- 
  cut(pData(target_Data)$GeneDetectionRate,
      breaks = c(0, 0.01, 0.05, 0.1, 0.15, 0.2,1),
      labels = c("<1%", "1-5%", "5-10%", "10-15%", "15-20%", ">20%"))

# stacked bar plot of different cut points (1%, 5%, 10%, 15%)
ggplot(pData(target_Data),
       aes(x = DetectionThreshold)) +
  geom_bar(aes(fill = ANN2)) +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  theme_bw() +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
  scale_fill_manual(values=color_list$ANN2) +
  labs(x = "Gene Detection Rate",
       y = "Segments, #",
       fill = "Segment Type")

cut percent genes detected at 1, 5, 10, 15

kable(table(pData(target_Data)$DetectionThreshold,
            pData(target_Data)$ANN2))
disease1B disease2B disease3 disease4 normal2B normal3 normal4
<1% 0 0 0 0 1 0 0
1-5% 0 1 0 0 6 0 0
5-10% 8 3 0 1 5 1 0
10-15% 13 11 1 3 2 3 0
15-20% 7 5 3 7 5 10 0
>20% 5 11 55 13 15 41 23
# set threshold for detectionlevel
# default 0.1
gene_det_threshold <- 0.05

target_Data <-
  target_Data[, pData(target_Data)$GeneDetectionRate >= gene_det_threshold]

dim(target_Data)
## Features  Samples 
##    18504      251

4.6 Manual removal of samples/classes

active_aois<-names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))

re-Collect annotations

# gather the data and plot in order: class, slide name, region, segment
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,ANN3,ANN4,slide_name)

temp_qc <- temp_df
temp_qc$QCResult <- QCResults$QCStatus
temp_qc$QCResult[temp_qc$QCResult == "WARNING"] <- "X"

#count_mat <- dplyr::count(a, ANN1, ANN2, slide_name, QCResult)
count_mat <- temp_qc %>% dplyr::count(temp_qc[c(ann_names, "QCResult")])

test_gr <- gather_set_data(count_mat, 1:(ann_size+1))
test_gr$x <- factor(test_gr$x, labels = c(ann_names, "QCResult"))

re-Plot Sankey

ggplot(test_gr, aes(x, id = id, split = y, value = n)) +
  geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.2) +
  geom_parallel_sets_labels(color = "white", size = 5) +
  theme_classic(base_size = 17) + 
  theme(legend.position = "bottom",
        axis.ticks.y = element_blank(),
        axis.line = element_blank(),
        axis.text.y = element_blank()) +
  scale_y_continuous(expand = expansion(0)) + 
  scale_x_discrete(expand = expansion(0)) +
  scale_fill_manual(values=color_list$ANN2) +
  labs(x = "", y = "") +
  annotate(geom = "segment", x = 3.25, xend = 3.25, y = 10, 
           yend = 60, lwd = 2) +
  annotate(geom = "text", x = 3.19, y = 25, angle = 90, size = 5,
           hjust = 0.5, label = "50 segments")

4.7 Gene Detection Rate

Calculate detection rate

LOQ_Mat <- LOQ_Mat[, colnames(target_Data)]
fData(target_Data)$DetectedSegments <- rowSums(LOQ_Mat, na.rm = TRUE)
fData(target_Data)$DetectionRate <-
  fData(target_Data)$DetectedSegments / nrow(pData(target_Data))

Gene of interest detection table

goi <- c("PDCD1", "CD274", "IFNG", "CD8A", "CD68", "EPCAM",
         "KRT18", "NPHS1", "NPHS2", "CALB1", "CLDN8")
goi_df <- data.frame(
  Gene = goi,
  Number = fData(target_Data)[goi, "DetectedSegments"],
  DetectionRate = percent(fData(target_Data)[goi, "DetectionRate"]))

4.8 Gene Filtering

We will graph the total number of genes detected in different percentages of segments. Based on the visualization below, we can better understand global gene detection in our study and select how many low detected genes to filter out of the dataset. Gene filtering increases performance of downstream statistical tests and improves interpretation of true biological signal.

Plot detection rate

plot_detect <- data.frame(Freq = c(1, 5, 10, 20, 30, 50))
plot_detect$Number <-
  unlist(lapply(c(0.01, 0.05, 0.1, 0.2, 0.3, 0.5),
                function(x) {sum(fData(target_Data)$DetectionRate >= x)}))
plot_detect$Rate <- plot_detect$Number / nrow(fData(target_Data))
rownames(plot_detect) <- plot_detect$Freq

ggplot(plot_detect, aes(x = as.factor(Freq), y = Rate, fill = Rate)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = formatC(Number, format = "d", big.mark = ",")),
            vjust = 1.6, color = "black", size = 4) +
  scale_fill_gradient2(low = "orange2", mid = "lightblue",
                       high = "dodgerblue3", midpoint = 0.65,
                       limits = c(0,1),
                       labels = scales::percent) +
  theme_bw() +
  scale_y_continuous(labels = scales::percent, limits = c(0,1),
                     expand = expansion(mult = c(0, 0))) +
  labs(x = "% of Segments",
       y = "Genes Detected, % of Panel > LOQ")

Subset to target genes detected in at least 10% of the samples. Also manually include the negative control probe, for downstream use

# default=0.1
negativeProbefData <- subset(fData(target_Data), CodeClass == "Negative")
neg_probes <- unique(negativeProbefData$TargetName)
target_Data <- 
  target_Data[fData(target_Data)$DetectionRate >= 0.05 |
                    fData(target_Data)$TargetName %in% neg_probes, ]

# retain only detected genes of interest
goi <- goi[goi %in% rownames(target_Data)]

dim(target_Data)
## Features  Samples 
##    12241      251

5 Normalization

We will now normalize the GeoMx data for downstream visualizations and differential expression. The two common methods for normalization of DSP-NGS RNA data are i) quartile 3 (Q3) or ii) background normalization.

Both of these normalization methods estimate a normalization factor per segment to bring the segment data distributions together. More advanced methods for normalization and modeling are under active development. However, for most studies, these methods are sufficient for understanding differences between biological classes of segments and samples.

Q3 normalization is typically the preferred normalization strategy for most DSP-NGS RNA studies. Given the low negative probe counts in this particular dataset as shown during Segment QC, we would further avoid background normalization as it may be less stable.

Before normalization, we will explore the relationship between the upper quartile (Q3) of the counts in each segment with the geometric mean of the negative control probes in the data. Ideally, there should be a separation between these two values to ensure we have stable measure of Q3 signal. If you do not see sufficient separation between these values, you may consider more aggressive filtering of low signal segments/genes.

Graph Q3 value vs negGeoMean of Negatives

ann_of_interest <- "ANN2"
Stat_data <- 
  data.frame(row.names = colnames(exprs(target_Data)),
             Segment = colnames(exprs(target_Data)),
             Annotation = pData(target_Data)[, ann_of_interest],
             Q3 = unlist(apply(exprs(target_Data), 2,
                               quantile, 0.75, na.rm = TRUE)),
             NegProbe = exprs(target_Data)[neg_probes, ])
Stat_data_m <- melt(Stat_data, measure.vars = c("Q3", "NegProbe"),
                    variable.name = "Statistic", value.name = "Value")

plt1 <- ggplot(Stat_data_m,
               aes(x = Value, fill = Statistic)) +
  geom_histogram(bins = 40) + theme_bw() +
  scale_x_continuous(trans = "log2") +
  facet_wrap(~Annotation, nrow = 1) + 
  scale_fill_brewer(palette = 3, type = "qual") +
  labs(x = "Counts", y = "Segments, #")

plt2 <- ggplot(Stat_data,
               aes(x = NegProbe, y = Q3, color = Annotation)) +
  geom_abline(intercept = 0, slope = 1, lty = "dashed", color = "darkgray") +
  geom_point() + guides(color = "none") + theme_bw() +
  scale_x_continuous(trans = "log2") + 
  scale_y_continuous(trans = "log2") +
  theme(aspect.ratio = 1) +
  labs(x = "Negative Probe GeoMean, Counts", y = "Q3 Value, Counts")

plt3 <- ggplot(Stat_data,
               aes(x = NegProbe, y = Q3 / NegProbe, color = Annotation)) +
  geom_hline(yintercept = 1, lty = "dashed", color = "darkgray") +
  geom_point() + theme_bw() +
  scale_x_continuous(trans = "log2") + 
  scale_y_continuous(trans = "log2") +
  theme(aspect.ratio = 1) +
  labs(x = "Negative Probe GeoMean, Counts", y = "Q3/NegProbe Value, Counts")

btm_row <- plot_grid(plt2, plt3, nrow = 1, labels = c("B", ""),
                     rel_widths = c(0.43,0.57))
plot_grid(plt1, btm_row, ncol = 1, labels = c("A", ""))

Q3 norm (75th percentile) for WTA/CTA with or without custom spike-ins

target_Data <- normalize(target_Data ,
                             norm_method = "quant", 
                             desiredQuantile = .75,
                             toElt = "q_norm")
#, data_type = "RNA" depricated after 4.1

Quantile Normalization

quantile <- normalize.quantiles(target_Data@assayData[["exprs"]])
rownames(quantile) <- rownames(target_Data@assayData[["exprs"]])
colnames(quantile) <- colnames(target_Data@assayData[["exprs"]])

assayDataElement(object = target_Data, elt = "quantile_norm") <-  quantile

Background normalization for WTA/CTA without custom spike-in

target_Data <- normalize(target_Data,
                             norm_method = "neg", 
                             fromElt = "exprs",
                             toElt = "neg_norm")

# , data_type = "RNA" depricated after 4.1

5.1 Visualize the first 10 segments with each normalization method

Use the tab-menu to navigate!

#Fix zero values, which go to -inf in log transform in standard boxplot
# temp <-as.matrix(exprs((target_Data)[,1:10]))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill="#9EDAE5") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Raw counts", x="segment", y = "Counts, Raw")
# 
# 
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "q_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill = "#2CA02C") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Q3 Norm Counts", x="segment", y = "Counts, Q3 Normalized")
# 
# 
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "neg_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill = "#FF7F0E") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Neg Norm Counts", x="segment", y = "Counts, Neg. Normalized")

raw counts

boxplot(exprs(target_Data)[,1:8],
        col = "#9EDAE5", main = "Raw Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Raw")

Q3 normalized

boxplot(assayDataElement(target_Data[,1:8], elt = "q_norm"),
        col = "#2CA02C", main = "Q3 Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Q3 Normalized")

Quantile normalized (testing phase)

boxplot(assayDataElement(target_Data[,1:8], elt = "quantile_norm"),
        col = "pink", main = "Quantile Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Quantile Normalized")

Negative probe normalization

boxplot(assayDataElement(target_Data[,1:8], elt = "neg_norm"),
        col = "#FF7F0E", main = "Neg Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Neg. Normalized")

6 Unsupervised Analysis

6.1 UMAP

Use the tab-menu to navigate!

1

custom_umap <- umap::umap.defaults
custom_umap$random_state <- 42
# run UMAP

umap_out <-
  umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

batch_correction_needed = FALSE

2

ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = ANN3, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.2 Batch correction

If the data is observed to have a batch effect it can be corrected with the methods: RUV4, LIMMA, or Combat-Seq. Each method is done and the best one is picked and used.

The standR package is short for Spatial transcriptomics analyzes and decoding in R, it aims at providing good practice pipeline and useful functions for users to analyze Nanostring’s GeoMx DSP data. In the Nanostring’s GeoMX DSP protocol, due to the fact that one slide is only big enough for a handful of tissue segments (ROIs), it is common that we see the DSP data being confounded by the batch effect introduced by different slides. In order to establish fair comparison between ROIs later on, it is necessary to remove this batch effect from the data. (https://bioconductor.org/packages/release/bioc/vignettes/standR/inst/doc/standR_introduction.html)

For RUV4 correction, the function is requiring 3 parameters other than the input object, including factors: the factor of interest, i.e. the biological variation we plan to keep; NCGs: the list of negative control genes detected using the function findNCGs; and k: is the number of unwanted factors to use, in the RUV documentation, it is suggest that we should use the smallest k once we don’t observe technical variation in the data.

Another option is set the parameter method to “Limma”, which uses the remove batch correction method from limma. In this mode, the function is requiring 2 parameters, including batch: a vector that indicating batches for all samples; and design: a design matrix which is generated by model.matrix, in the design matrix, all biologically-relevant factors should be included.

ComBat-seq is a batch effect adjustment tool for bulk RNA-seq count data. It is an improved model based on the popular ComBat, to address its limitations through novel methods designed specifically for RNA-Seq studies. ComBat-seq takes untransformed, raw count matrix as input. Same as ComBat, it requires a known batch variable. (https://github.com/zhangyuqing/ComBat-seq)

Set up standR object

count_geomx <- as.data.frame(target_Data@assayData[["exprs"]])

sample_geomx <- target_Data@phenoData@data
sample_geomx$roi <- target_Data@protocolData@data$roi
sample_geomx <- as.data.frame(sample_geomx)
sample_geomx$Sample_ID <- rownames(sample_geomx)
sample_geomx$SegmentDisplayName <- paste(sample_geomx$`scan name`, sample_geomx$roi, sample_geomx$segment, sep = " | ")
sample_geomx$ROICoordinateX <- 1
sample_geomx$ROICoordinateY <- 1


feature_geomx <- fData(Data)
feature_geomx <- feature_geomx[c("RTS_ID", "TargetName", "ProbeID", "Negative")]
feature_geomx <- as.data.frame(feature_geomx)
rownames(feature_geomx) <- NULL

matching <- sample_geomx$SegmentDisplayName[sample_geomx$Sample_ID %in% colnames(count_geomx)]
colnames(count_geomx) <- matching
count_geomx$TargetName <- rownames(count_geomx)
rownames(count_geomx) <- NULL

spe <- readGeoMx(count_geomx, sample_geomx, featureAnnoFile = feature_geomx, hasNegProbe = TRUE)

colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |> 
  (\(.) gsub("_Geometric Segment","",.))() |>
  paste0("_",colData(spe)$pathology) |>
  (\(.) gsub("_NA","_ns",.))()

colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |> 
  (\(.) gsub("_Geometric Segment","",.))() |>
  paste0("_",colData(spe)$pathology) |>
  (\(.) gsub("_NA","_ns",.))()
colData(spe)$biology <- paste0(colData(spe)$regions)

See optimal k value for RUV4

spe <- findNCGs(spe, batch_name = "slide_name", top_n = 500)
findBestK(spe, maxK = 10, factor_of_int = "biology", NCGs = metadata(spe)$NCGs, factor_batch = "slide_name")

RUV4

ruv4 <- geomxBatchCorrection(spe, factors = "biology", 
                   NCGs = metadata(spe)$NCGs, k = 1)

plotPairPCA(ruv4, assay = 2, color = slide_name, shape = regions, title = "RUV4 removeBatch")

plotRLExpr(ruv4, assay = 2, color = `slide_name`) + ggtitle("RUV4 removeBatch")

Limma

limma <- geomxBatchCorrection(spe,
                       batch = colData(spe)$`slide_name`, method = "Limma",
                       design = model.matrix(~ 0 + ANN1 + regions, 
                                             data = colData(spe)))

plotPairPCA(limma, assay = 2, color = slide_name, shape = regions, title = "Limma removeBatch")

plotRLExpr(limma, assay = 2, color = slide_name) + ggtitle("Limma removeBatch")

CombatSeq

adjusted <- ComBat_seq(target_Data@assayData[["exprs"]], batch=target_Data@phenoData@data[["slide_name"]], group=target_Data@phenoData@data[["ANN2"]])
assayDataElement(object = target_Data, elt = "combat") <-  adjusted

umap_out2 <-
  umap(t(log2(assayDataElement(target_Data , elt = "combat"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out2$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw() + ggtitle("CombatSeq removeBatch")

Compare limma vs RUV4

spe_list <- list(spe, ruv4, limma)

plotClusterEvalStats(spe_list = spe_list,
                     bio_feature_name = "regions",
                     batch_feature_name = "slide_name",
                     data_names = c("Raw","RUV4","Limma"))

Add batch correction result to target_Data

neg_probes_save <- t(as.matrix(target_Data@assayData$q_norm["NegProbe-WTX",]))
rownames(neg_probes_save) <- "NegProbe-WTX"

# Depending on correct method, change the word "limma" to "ruv4" or vice versa.
limma <-limma@assays@data@listData[["logcounts"]]
limma <- rbind(limma, neg_probes_save)
colnames(limma) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "limma") <-  limma

ruv4 <-ruv4@assays@data@listData[["logcounts"]]
ruv4 <- rbind(ruv4, neg_probes_save)
colnames(ruv4) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "ruv4") <-  ruv4

Choose method

# choose method to replace q_norm or set it to ""
method <- ""

# replace q_norm with chosen method
if (!method == "") {
  # save orginal q_norm
  assayDataElement(object = target_Data, elt = "original") <-  assayDataElement(object = target_Data, elt = "q_norm")
  
  assayDataElement(object = target_Data, elt = "q_norm") <-  assayDataElement(object = target_Data, elt = method)
  print(paste("Batch correction method:", method))
} else {print("No batch correction needed")}
## [1] "No batch correction needed"

Umap after batch correction

umap_out <-
  umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.3 Run tSNE

Use the tab-menu to navigate!

One common approach to understanding high-plex data is dimension reduction. Two common methods are UMAP and tSNE, which are non-orthogonally constrained projections that cluster samples based on overall gene expression. In this study, we see by either UMAP (from the umap package) or tSNE (from the Rtsne package)

1

tsne_out <-
  Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
        perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = tSNE1, y = tSNE2, shape = slide_name, color = ANN2)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

2

tsne_out <-
  Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
        perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = tSNE1, y = tSNE2, color = ANN3, shape = ANN1)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.4 Clustering high CV Genes

Another approach to explore the data is to calculate the coefficient of variation (\(CV\)) for each gene (\(g\)) using the formula \(CV_g=SD_g/mean_g\). We then identify genes with high CVs that should have large differences across the various profiled segments. This unbiased approach can reveal highly variable genes across the study.

We plot the results using unsupervised hierarchical clustering, displayed as a heatmap.

# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data,
                         elt = "log_q", MARGIN = 1, calc_CV)
# show the highest CD genes and their CV values
sort(CV_dat, decreasing = TRUE)[1:5]
##   CAMK2N1    AKR1C1      AQP2       REN     GDF15 
## 0.6344192 0.5238995 0.4752667 0.4414027 0.4276390

Table of CV values

# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
          extensions = 'Buttons', options = list (
            order = list(1, 'desc'),
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          ), caption = "CV values of genes" 
) %>% formatRound(columns=c("CV_dat"), digits=3)

Heatmap genes in the top 3rd of the CV values

GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]

pheatmap(assayDataElement(target_Data[GOI, ], elt = "log_q"),
         scale = "row",
         cutree_cols = 3,
         cutree_rows = 2,
         show_rownames = FALSE, show_colnames = TRUE,
         border_color = NA,
         drop_levels = TRUE,
         clustering_method = "average",
         clustering_distance_rows = "correlation",
         clustering_distance_cols = "correlation",
         breaks = seq(-3, 3, 0.05),
         color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

assayDataElement(object = target_Data, elt = "log_q") <-  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
log_q <-as.data.frame(assayDataElement(target_Data, elt= "log_q"))
#batch <-as.data.frame(assayDataElement(target_Data, elt= "batch"))

6.5.0 Create subset of data

# determine AOIs to use
#active_aois<-rownames(ann)[ann$patient=="p4"]
active_aois<- names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))

6.5.1 Clustering high CV genes for subset

Calculating CV values

# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data[,active_aois],
                         elt = "log_q", MARGIN = 1, calc_CV)

Table of CV values

# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
          extensions = 'Buttons', options = list (
            order = list(1, 'desc'),
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          ), caption = "CV values of genes" 
) %>% formatRound(columns=c("CV_dat"), digits=3)

Heatmap on of subset, genes in the top 3rd of the CV values

# Identify genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI,active_aois ], elt = "log_q"),
        scale = "row",
        fontsize_row = 5,
        cutree_cols = 3,
        cutree_rows = 2,
        show_rownames = FALSE, show_colnames = TRUE,
        border_color = NA,
        clustering_method = "average",
        clustering_distance_rows = "correlation",
        clustering_distance_cols = "correlation",
        breaks = seq(-3, 3, 0.05),
        color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
       annotation_colors = color_list,
        annotation_col =
          pData(target_Data)[, ann_names])

7.1 Differential Expression

t-test

#gc()
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BY"
#options: "holm"       "hochberg"   "hommel"     "bonferroni" "BH"         "BY"         "fdr" 
counter=1

comps_df<-data.frame(comp='',val='')

for(region in unique(pData(target_Data)$ANN3)) {
  for (active_group1 in unique(pData(target_Data)$ANN1)) {
    for (active_group2 in unique(pData(target_Data)$ANN1)) {
      
      #supress reduncant compares
      if(active_group1==active_group2) {next}
      comp<-paste(sort(c(region, active_group1,active_group2)),collapse = "_")
      if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)
      
      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group1 & pData(target_Data)$ANN3==region]]
      group2<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group2 & pData(target_Data)$ANN3==region]]
      
      #run t_tests  
      results<-as.data.frame ( apply(log_q, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"
      
      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)
      
      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)
      
      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame(apply(log_q, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]))))
      colnames(fchanges)<-"FC"
      #paste("FC",active_group1," / ",active_group2)
      results<-cbind(results,fchanges)
      
      #add genenames
      results$Gene<-rownames(results)
      
      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 1"
      results$Color[results$raw_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) <1] <- "NS or FC < 1"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 1", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
      
      #vulcanoplot
      
      # pick top genes for either side of volcano to label
      # order genes for convenience:
      
      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[, 'Gene'][
                   order(results[, 'invert_P'], decreasing = TRUE)[1:20]],
                 results[, 'Gene'][order(results[, 'invert_P'], decreasing = FALSE)[1:20]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix
      
      # Graph results
      
      plots[[counter]]<- ggplot(results,
                                      aes(x = FC, y = -log10(raw_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(1, -1), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched in", active_group2," <- log2(FC) -> Enriched in", active_group1),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, (-1>FC| FC>1) & FDR < 0.05 & Gene %in% top_g),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 10) +
        theme(legend.position = "bottom") +
        ggtitle(paste("class: ", region," - ", test, mtc,"multitest corr"))
      
      #store tables for display later
      tables[[counter]]<-results
      
      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
    }
  }
}
grid.arrange(grobs=plots,ncol=2)

#strangly does not appear in html output -> chucks are limited to one datatable per chuck. The htmltools are a way around this but does show them stacked in a box.
results_tables <- htmltools::tagList()
for (c in (2:counter-1)) {
  #Gene %in% GOI
  results_tables[[c]] <- datatable(subset(tables[[c]], Color == c("FDR < 0.001",  "P < 0.05")), 
           rownames=FALSE,
           extensions = c('Buttons'), options = list (
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ), height = 550,
           caption = paste("DE results ", labels[[c]]),filter='top') %>% formatRound(columns=c("raw_p_value","adj_p_value","FDR","FC"), digits=3)
  #cat('\n\n<!-- -->\n\n')
}            
results_tables
print("") # for layout of next section
## [1] ""

7.2 DE analysis with LMM

A common statistical approach is to use a linear mixed-effect model (LMM). The LMM allows the user to account for the subsampling per tissue; in other words, we adjust for the fact that the multiple regions of interest placed per tissue section are not independent observations, as is the assumption with other traditional statistical tests. The formulation of the LMM model depends on the scientific question being asked.

Overall, there are two flavors of the LMM model when used with GeoMx data: i) with and ii) without random slope.

When comparing features that co-exist in a given tissue section, a random slope is included in the LMM model. When comparing features that are mutually exclusive in a given tissue section the LMM model does not require a random slope.

Mostly, we use patient/sample as Random Intercept when they are combined on slides.

# convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(target_Data)[["slide"]] <- factor(pData(target_Data)[["slide_name"]])
#assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# run LMM:
# formula follows conventions defined by the lme4 package
lmm_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
    ind <- pData(target_Data)$ANN3 == region

    mixedOutmc <-
        mixedModelDE(target_Data[,ind],
                     elt = "log_q",
                     modelFormula = ~ testClass + (1 | slide),
                     groupVar = "testClass",
                     nCores = parallel::detectCores(),
                     multiCore = TRUE)

    # format results as data.frame
    r_test <- do.call(rbind, mixedOutmc["lsmeans", ])
    tests <- rownames(r_test)
    r_test <- as.data.frame(r_test)
    r_test$Contrast <- tests

    # use lapply in case you have multiple levels of your test factor to
    # correctly associate gene name with it's row in the results table
    r_test$Gene <-
        unlist(lapply(colnames(mixedOutmc),
                      rep, nrow(mixedOutmc["lsmeans", ][[1]])))
    r_test$Subset <- region
    r_test$FDR <- p.adjust(r_test$`Pr(>|t|)`, method = "fdr")
    r_test <- r_test[, c("Gene", "Subset", "Contrast", "Estimate",
                         "Pr(>|t|)", "FDR")]
    
    lmm_results <- rbind(lmm_results, r_test)
}

7.3 Vulcanoplot + table of LMM

# Categorize Results based on P-value & FDR for plotting
fc_threshold = 1

lmm_results$Color <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color[lmm_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_results$Color[lmm_results$FDR < 0.05] <- "FDR < 0.05"
lmm_results$Color[lmm_results$FDR < 0.001] <- "FDR < 0.001"
lmm_results$Color[abs(lmm_results$Estimate) < fc_threshold] <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color <- factor(lmm_results$Color,
                        levels = c("NS or FC < 1", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))
counter = 1
plots_lmm <- list()
for(c in unique(lmm_results$Subset) ) {
  lmm_results_slice = lmm_results[lmm_results$Subset == c,]

  # pick top genes for either side of volcano to label
  # order genes for convenience:
  lmm_results_slice$invert_P <- (-log10(lmm_results_slice$`Pr(>|t|)`)) * sign(lmm_results_slice$Estimate)
  
  #loop here over tested conditions if applicable
  top_g <- c()
  top_g <- c(top_g,
    lmm_results_slice[, 'Gene'][
      order(lmm_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
    lmm_results_slice[, 'Gene'][
      order(lmm_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])

  top_g <- unique(top_g)
  
  lmm_results_slice <- lmm_results_slice[, -1*ncol(lmm_results_slice)] # remove invert_P from matrix
  
  # Flip Contrast
  contrast_lab <- as.character(lmm_results_slice$Contrast)
  contrast_lab <- strsplit(contrast_lab, "-")[[1]]
  contrast_lab <- paste(contrast_lab[2], "-", contrast_lab[1])
  
  # Graph results
  plots_lmm[[counter]] <- ggplot(lmm_results_slice,
               aes(x = Estimate, y = -log10(`Pr(>|t|)`),
                   color = Color, label = Gene)) +
          geom_vline(xintercept = c(fc_threshold, -fc_threshold), lty = "dashed") +
          geom_hline(yintercept = -log10(0.05), lty = "dashed") +
          geom_point() +
          labs(
            x = paste(contrast_lab, " log2(FC)"),
               y = "Significance, -log10(P)",
               color = "Significance") +
          scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                        `FDR < 0.05` = "lightblue",
                                        `P < 0.05` = "orange2",
                                        `NS or FC < 1` = "gray"),
                             guide = guide_legend(override.aes = list(size = 4))) +
          scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
          geom_text_repel(data = subset(lmm_results_slice,  `Pr(>|t|)` < 0.001 & abs(lmm_results_slice$Estimate) > fc_threshold & Gene %in% top_g),
                          point.padding = 0.15, color = "black",size=5,
                          min.segment.length = .1, box.padding = .2, lwd = 2,
                          max.overlaps = 50) +
          theme_bw(base_size = 16) +
          theme(legend.position = "bottom") +
          facet_wrap(~Subset, scales = "free_y")
  counter <- counter + 1
}
grid.arrange(grobs=plots_lmm,ncol=2)

#subset(lmm_results, Gene %in% GOI)
datatable(lmm_results, rownames = FALSE,
          extensions = 'Buttons', options = list (
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ),
          caption = "DE results for Genes of Interest (>75% CV)",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)

7.4 Plotting Genes of Interest

my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)
tmp_tbl<-subset(lmm_results, Gene %in% my_gois)

my_gois <- c()
for (contrast in unique(tmp_tbl$Contrast)) { # gene of interest for all contrasts
#for (contrast in c("Jux_Glo - Pro_Tub")) {
  ind <- tmp_tbl$Contrast == contrast
  goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = FALSE)[1:3]]
  my_gois <- c(my_gois, goi)
  goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = TRUE)[1:3]]
  my_gois <- c(my_gois, goi)
}

if(nrow(tmp_tbl) > 1) { 
  datatable(tmp_tbl,rownames = FALSE,caption = "DE results for Genes of Interest ",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
 
for (my_goi in my_gois) {
# show expression for a single target
  a<-ggplot(pData(target_Data),
       aes(x = ANN2, fill = ANN2,
           y = as.numeric(assayDataElement(target_Data[my_goi, ], elt = "q_norm")))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = paste(my_goi,"Expression")) +
  scale_y_continuous(trans = "log2") +
  #facet_wrap(~ANN3, nrow=1) + theme_bw(base_size = 16) +
  theme_bw() + coord_flip()
  print(a)
}
}else{
  print("No significant lMM results to plot")
}

7.5 Heatmap of Significant Genes

In addition to generating individual gene box plots or volcano plots, we can again create a heatmap from our data. This time rather than utilizing CV to select genes, we can use the P-value or FDR values to select genes. Here, we plot all genes with an FDR < 0.001.

my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)   # 100 to prevent long runtime

if(length(my_gois)<2) {
  print("No significant results to show")
 
}else{

pheatmap(log2(assayDataElement(target_Data[my_gois, ], elt = "q_norm")),
         scale = "row",
         show_rownames = TRUE, show_colnames = TRUE,
         border_color = NA,
         clustering_method = "average",
         clustering_distance_rows = "correlation",
         clustering_distance_cols = "correlation",
         cutree_cols = 3, cutree_rows = 2,
         breaks = seq(-3, 3, 0.05),
         color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])
}

8 Pathway Analysis

Pathway analysis enables exploration of different aggregate gene sets for our experimental questions. Each individual ROI/AOI segment is scored for every pathway of interest, which we can then use to investigate biological differences. We will perform analogous analyses as those outlined in the Differential Expression and Spatial Deconvolution sections of the report for gene set enrichment.

8.1 Scoring Gene Sets

Pathways and gene sets were defined from the Kegg Brite database. We use an R software package called Gene Set Variation Analysis to score each segment within our study. see https://cran.r-project.org/web/packages/msigdbr/vignettes/msigdbr-intro.html for options on collections. We use the KEGG and REACTOME.

#gc()
h_gene_sets = rbind(msigdbr(species = "human", subcategory = "CP:KEGG"),
                    msigdbr(species = "human", subcategory = "CP:REACTOME"))
#msigdbr(species = "human", subcategory = "CP:BIOCARTA")

msigdbr_list = split(x = h_gene_sets$gene_symbol, f = h_gene_sets$gs_name)

# prepare df for accurate merging back genes later
pw_gene_df<-data.frame(Pathway = names(msigdbr_list))
pw_gene_df$genes<-msigdbr_list
ssgsea_results <- GSVA::gsva(expr = assayDataElement(target_Data,
                            elt = "log_q"),
                            gset.idx.list = msigdbr_list,
                            method = "ssgsea",
                            min.sz = 5,
                            max.sz = 500,
                            verbose = FALSE)
## [1] "Calculating ranks..."
## [1] "Calculating absolute values from ranks..."
## [1] "Normalizing..."
geneSetObj <-
  NanoStringGeoMxSet(assayData = ssgsea_results,
                     phenoData = AnnotatedDataFrame(pData(target_Data)),
                     protocolData = protocolData(target_Data),
                     featureType = "GeneSet",
                     check = FALSE)

8.2 Differental analysis of pathways

# # convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(geneSetObj)[["slide"]]<-factor(pData(geneSetObj)[["slide_name"]])
#pData(target_Data)$testRegion<-factor(pData(target_Data)$ANN2, unique(count_mat$ANN3))

lmm_ssgsea_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
  ind <- pData(target_Data)$ANN3 == region
  mixedOutmc <-
    mixedModelDE(geneSetObj[, ind],
                 elt = "exprs",
                 modelFormula = ~ testClass + (1 | slide),
                 groupVar = "testClass",
                 nCores = parallel::detectCores(),
                 multiCore = TRUE)

  # format results as data.frame
  r_ssgsea_test <- do.call(rbind, mixedOutmc["lsmeans", ])
  ssgsea_tests <- rownames(r_ssgsea_test)
  r_ssgsea_test <- as.data.frame(r_ssgsea_test)
  r_ssgsea_test$Contrast <- ssgsea_tests
  #r_ssgsea_test$Genes <- msigdbr_list seems unreliable as gsva omits pathways if genes are not in data..

  # use lapply in case you have multiple levels of your test factor to
  # correctly associate gene name with it's row in the results table
  r_ssgsea_test$Pathway <-
    unlist(lapply(colnames(mixedOutmc),
                  rep, nrow(mixedOutmc["lsmeans", ][[1]])))

  #r_ssgsea_test$Subset <- status
  r_ssgsea_test$FDR <- p.adjust(r_ssgsea_test$`Pr(>|t|)`, method = "fdr")
  r_ssgsea_test$Subset <- region
  r_ssgsea_test <- r_ssgsea_test[, c("Pathway", "Subset", "Contrast", "Estimate",
                                     "Pr(>|t|)", "FDR")]
  lmm_ssgsea_results <- rbind(lmm_ssgsea_results, r_ssgsea_test)

  }
  lmm_ssgsea_results <- merge(lmm_ssgsea_results, pw_gene_df)

8.2.1 Table of Differental analysis of pathways

datatable(subset(lmm_ssgsea_results), rownames = FALSE,
          extensions = 'Buttons', options = list (
             pageLength = 10,
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ),
          caption = "DE results for Pathways",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=5)

8.3 Vulcanoplot of LMM_Pathways

# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3

lmm_ssgsea_results$Color <- "NS or FC < 0.3"
lmm_ssgsea_results$Color[lmm_ssgsea_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.05] <- "FDR < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.001] <- "FDR < 0.001"
lmm_ssgsea_results$Color[abs(lmm_ssgsea_results$Estimate) < fc_threshold] <- "NS or FC < 0.3"
lmm_ssgsea_results$Color <- factor(lmm_ssgsea_results$Color,
                        levels = c("NS or FC < 0.3", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))

# pick top pw for either side of volcano to label
# order pw for convenience:
lmm_ssgsea_results$invert_P <- (-log10(lmm_ssgsea_results$`Pr(>|t|)`)) * sign(lmm_ssgsea_results$Estimate)
top_ssgsea_g <- c()

#loop here over tested conditions if applicable

for(c in unique(lmm_ssgsea_results$Subset) ) {
  lmm_results_slice = lmm_ssgsea_results[lmm_ssgsea_results$Subset == c,]
 top_ssgsea_g <- c(top_ssgsea_g,
         lmm_ssgsea_results[ind, 'Pathway'][
               order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
         lmm_ssgsea_results[ind, 'Pathway'][
               order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
}
 
top_ssgsea_g <- unique(top_ssgsea_g)
lmm_ssgsea_results <- lmm_ssgsea_results[, -1*ncol(lmm_ssgsea_results)] # remove invert_P from matrix

#lmm_ssgsea_results_slice <- lmm_ssgsea_results_slice[lmm_ssgsea_results_slice$FDR < 1,]

# Graph results
ggplot(lmm_ssgsea_results,
       aes(x = Estimate, y = -log10(`Pr(>|t|)`),
           color = Color, label = Pathway)) +
    geom_vline(xintercept = c(0.2, -0.2), lty = "dashed") +
    geom_hline(yintercept = -log10(0.05), lty = "dashed") +
    geom_point() +
    labs(x = paste(lmm_results_slice$Contrast, "log2(FC)"),
         y = "Significance, -log10(P)",
         color = "Significance") +
    scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                  `FDR < 0.05` = "lightblue",
                                  `P < 0.05` = "orange2",
                                  `NS or FC < 0.5` = "gray"),
                       guide = guide_legend(override.aes = list(size = 4))) +
    scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
    geom_text_repel(data = subset(lmm_ssgsea_results, Color == "FDR < 0.05" | Color == "FDR < 0.001"),
                   point.padding = 0.15, color = "black",size=3,
                   min.segment.length = .1, box.padding = .2, lwd = 2,
                   max.overlaps = 50) +
    theme_bw(base_size = 16) +
    theme(legend.position = "bottom") +
    facet_wrap(~Subset, scales = "free_y")

#    +facet_wrap(~Subset, scales = "free_y"))

8.4 heatmap of pathways

  active_pw<-filter(lmm_ssgsea_results, `Pr(>|t|)` < 0.001)$Pathway
  active_pw<-filter(lmm_ssgsea_results, FDR < 0.001 )$Pathway
  #active_pw<-filter(lmm_ssgsea_results, Color == "FDR < 0.001")$Pathway
  
  
  active_pw<-top_ssgsea_g
  
  if (length(active_pw)>1) {
  
    print("go")
    
  pw_matrix<-assayDataElement(geneSetObj, elt = "exprs")

  pheatmap(pw_matrix[active_pw,],
         scale = "row",
         show_rownames = TRUE, show_colnames = TRUE,
         fontsize_row = 10,
         border_color = NA,
         clustering_method = "average",
         #clustering_distance_rows = "correlation",
         clustering_distance_cols = "euclidean",
         cutree_cols = 2, cutree_rows = 2,
         breaks = seq(-3, 3, 0.05),
         #color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         main = "Heatmap of selected Pathways",
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])
  
  }else{
    print("No significant results to display")
  }
## [1] "go"

8.5 fgsea pathway analysis

Another option for pathway analysis is the R software package called Fast Gene Set Enrichment Analysis. It is a pre-ranked method that uses the LMM results combined with the same pathway list from msigdbr as GSVA.

fgsea_results_all <- c()
for (contrast in unique(lmm_results$Contrast)) {
    
    ranks <- lmm_results$Estimate[lmm_results$Contrast == contrast]
    names(ranks) <- lmm_results$Gene[lmm_results$Contrast == contrast]
    
    fgsea_results <- fgsea(msigdbr_list, 
                         ranks, 
                         eps = 0.0,
                         minSize=15, 
                         maxSize = 500)
    fgsea_results$Contrast <- contrast
    fgsea_results_all <- rbind(fgsea_results_all, fgsea_results)
    
}

fgsea_reactome <- fgsea_results_all[grep("REACTOME", pathway),]
fgsea_reactome$pathway <- gsub("REACTOME_", "", fgsea_reactome$pathway)
fgsea_reactome$pathway <- gsub("_", " ", fgsea_reactome$pathway)
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3


fgsea_results_all$Color <- "NS or FC < 0.3"
fgsea_results_all$Color[fgsea_results_all$pval < 0.05] <- "P < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.05] <- "FDR < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.001] <- "FDR < 0.001"
fgsea_results_all$Color[abs(fgsea_results_all$ES) < fc_threshold] <- "NS or FC < 0.3"
fgsea_results_all$Color <- factor(fgsea_results_all$Color,
                        levels = c("NS or FC < 0.3", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))

plt <- htmltools::tagList()
counter = 1
for(c in unique(fgsea_results_all$Contrast) ) {
  fgsea_results_slice = fgsea_results_all[fgsea_results_all$Contrast == c,]

  # # pick top genes for either side of volcano to label
  # # order genes for convenience:
  fgsea_results_slice$invert_P <- (-log10(fgsea_results_slice$pval)) * sign(fgsea_results_slice$ES)
  
  # #loop here over tested conditions if applicable
  #top_fgsea_g <- c()
  top_fgsea_g <- c(fgsea_results_slice[, 'pathway'][
                        order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
                    fgsea_results_slice[, 'pathway'][
                        order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
   
  # for (celltype in unique(lmm_results$Subset)) {
  #      top_fgsea_g <- c(top_fgsea_g,
  #                  fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
  #                      order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:20]],
  #                  fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
  #                      order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:20]])
  # }
  
  top_fgsea_g <- unique(top_fgsea_g)
  #fgsea_results_slice <- fgsea_results_slice[, -1*ncol(fgsea_results_slice)] # remove invert_P from matrix
  
  # Graph results
  dynplot <- ggplot(fgsea_results_slice,
         aes(x = NES, y = -log10(pval),
             color = Color, label = pathway)) +
      geom_vline(xintercept = c(fc_threshold,-fc_threshold), lty = "dashed") +
      geom_hline(yintercept = -log10(0.05), lty = "dashed") +
      geom_point() +
      labs(x = paste(c, " FC"),
           y = "Significance, -log10(P)",
           color = "Significance") +
      scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                    `FDR < 0.05` = "lightblue",
                                    `P < 0.05` = "orange2",
                                    `NS or FC < 0.3` = "gray"),
                         guide = guide_legend(override.aes = list(size = 4))) +
      scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
      geom_text_repel(data = subset(fgsea_results_slice, Color == "P < 0.05" | Color == "FDR < 0.05" | Color == "FDR < 0.001"),
                     point.padding = 0.15, color = "black",size=5,
                     min.segment.length = .1, box.padding = .2, lwd = 2,
                     max.overlaps = 50) +
      theme_bw(base_size = 16) +
      theme(legend.position = "bottom") #+
      #facet_wrap(~Subset, scales = "free_y")
   
  plt[[counter]] <- as_widget(ggplotly(dynplot))
  counter <- counter + 1
  #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
plt

8.6 Visualize KEGG pathway

Get pathway visualization from KEGG. clusterProfiler grabs the KEGG pathway IDs and combined with pathview it can download a pathway image from the KEGG database. The LMM results are given with the pathway id to visualize up and down regulated genes.

get_wp_gmtfile <- function() {
    wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
    x <- readLines(wpurl)
    y <- x[grep('\\.gmt',x)]
    sub(".*(wikipathways-.*\\.gmt).*", "\\1",  y[grep('File', y)])
}

get_wp_data <- function(organism) {
    organism <- sub(" ", "_", organism)
    gmtfile <- get_wp_gmtfile()
    wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
    url <- paste0(wpurl,
                  gmtfile[grep(organism, gmtfile)])
    f <- tempfile(fileext = ".gmt")
    dl <- mydownload(url, destfile = f)
    if (is.null(f)) {
        message("fail to download wikiPathways data...")
        return(NULL)
    }
    read.gmt.wp(f)
}
# enrichKEGG grab online pathways
gene <-  target_Data@featureData@data[["GeneID"]]
kk <- enrichKEGG(gene         = gene,
                 organism     = 'hsa',
                 pvalueCutoff = 0.1)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
enrichKEGG_results <- kk@result

# Match column values for link
# fsgea run
fgsea_results$Description <- gsub("KEGG_", "", fgsea_results$pathway)
fgsea_results$Description <- gsub("_", " ", fgsea_results$Description)
# ssgea run
# lmm_ssgsea_results_d$Description <- gsub("KEGG_", "", lmm_ssgsea_results_d$Pathway)
# lmm_ssgsea_results_d$Description <- gsub("_", " ", lmm_ssgsea_results_d$Description)

enrichKEGG_results$Description <- toupper(enrichKEGG_results$Description)

# Create df for matching KEGG results
KEGGIDs <- fgsea_results#[fgsea_results$Contrast == "Pro_Tub - Dis_Tub"]
KEGGIDs <- KEGGIDs %>% inner_join(enrichKEGG_results, by = 'Description') %>% select(Description, ID, geneID, leadingEdge, padj)

# Create geneList with DE results for visualization pathway genes 
genelist <- lmm_results$Estimate
names(genelist) <- target_Data@featureData@data[["GeneID"]]

# Choose manual or highest significant pathway
pathway_name <- "INSULIN SIGNALING PATHWAY" # manual pathway query
pathway_name <- toupper(pathway_name)
if (pathway_name == "") {
  pathway_name <- KEGGIDs[order(KEGGIDs$padj, decreasing = FALSE), ][1]$Description
}
pathwayid <- KEGGIDs$ID[KEGGIDs$Description == pathway_name]

# Grab pathway image with gene info, save and plot
viewPath <- pathview(gene.data  = genelist,
                     pathway.id = pathwayid,
                     species    = "hsa",
                     out.suffix = "genesinfo", kegg.native = T, same.layer = F)
## Info: Downloading xml files for hsa04910, 1/1 pathways..
## Info: Downloading png files for hsa04910, 1/1 pathways..
## 'select()' returned 1:1 mapping between keys and columns
## Info: Working in directory C:/Users/pkloosterman/Documents/GitHub/GeoMX-analysis/Data/DKD_Kidney
## Info: Writing image file hsa04910.genesinfo.png
img <- readPNG(paste(pathwayid, ".genesinfo.png", sep = ""))
grid::grid.raster(img)

Barplot pathways

Clear vizualization of the top 15 positive and negative expressed Reactome pathways. Pathways with a adjusted p-value of 0.05 or higher will be presented as red and below 0.05 as green.

top_all <- c()
for (contrast in unique(fgsea_reactome$Contrast)) {
    # active_group1 <- contrast_list[contrast][[1]][[1]]
    # active_group2 <- contrast_list[contrast][[1]][[2]]
    # ind <- pData(target_Data)[pData(target_Data)$ANN2 == active_group1 | pData(target_Data)$ANN2 == active_group2]
    top <- fgsea_reactome[fgsea_reactome$Contrast == contrast]
    toppos <- top[order(top$NES, decreasing = TRUE)][0:15]
    topneg <- top[order(top$NES, decreasing = FALSE)][0:15]
    top <- rbind(toppos, topneg)
    
    top$Color[top$padj < 0.05] <- "padj < 0.05"
    top$Color[top$padj == 0.05 | top$padj > 0.05] <- "padj > 0.05"
    top_all <- rbind(top_all, top)
    barplot <- ggplot(top,aes(x=reorder(pathway, NES),y=NES,fill=Color)) + 
      scale_x_discrete(labels = function(x) str_wrap(x, width = 100)) + 
      geom_col(position="dodge") + scale_fill_manual(values = c("#2ECC71", "#E74C3C")) +
      ggtitle(paste("Top 15 + and - NES reactome pathways from", contrast)) + 
      xlab("pathway") + ylab("Normalized Enrichment Score") +
      coord_flip() + 
      theme(text = element_text(size = 20)) +
      theme(panel.background = element_blank())
      #facet_wrap(~Contrast, scales = "free_y")
    print(barplot)
}

9 Spatial Deconvolution

9.1 Calculate backgrounds

#gc()
bg = derive_GeoMx_background(
  norm = assayDataElement(target_Data , elt = "q_norm"),
  probepool = fData(target_Data)$Module,
  negnames = c("NegProbe-CTP01","NegProbe-Kilo","Negative Probe", "NegProbe-WTX" ))
  #negnames = "NegProbe-WTX")

9.2 Load cell profile

A “cell profile matrix” is a pre-defined matrix that specifies the expected expression profiles of each cell type in the experiment. The SpatialDecon library comes with one such matrix pre-loaded, the “SafeTME” matrix, designed for estimation of immune and stroma cells in the tumor microenvironment. (This matrix was designed to avoid genes commonly expressed by cancer cells; see the SpatialDecon manuscript for details.). Otherwise, load specific profiles from https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices

#safeTME
data("safeTME")
data("safeTME.matches")
current_cell_profile<-safeTME

#see: https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices

current_cell_profile <- download_profile_matrix(species = "Human",
                                               age_group = "Adult",
                                               matrixname = "Kidney_HCA")

heatmap(sweep(current_cell_profile, 1, apply(current_cell_profile, 1, max), "/"),
        labRow = NA, margins = c(10, 5), cexCol = 0.7)

9.3 Run spatial deconvolution

# vector identifying pure tumor segments:
#target_Data$istumor = target_Data$ANN3 == "CORE" & target_Data$ANN1 == "PanCK+"
res = runspatialdecon(object = target_Data,
                      norm_elt = "q_norm",
                      raw_elt = "exprs",
                      #is_pure_tumor = target_Data$istumor,
                      cell_counts = target_Data$nuclei,
                      X = current_cell_profile,
                      #cellmerges = safeTME.matches,              # safeTME.matches object, used by default
                      #n_tumor_clusters = 5,                      # how many distinct tumor profiles to append to safeTME
                      align_genes = TRUE)

9.3.1 Spatial deconvolution heatmaps

Abundance

# NOTE: check clustering.. why different?

#set display thresholds
thresh <- signif(quantile(res$beta, 0.97), 2)

# plot stored to keep clustering for later
p1<-pheatmap(pmin(t(res$beta),thresh),
         #scale = "row", 
         cutree_cols = 3,
         cutree_rows = 2,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = c(round(seq(0, thresh, length.out = 5))[-5], thresh),
         legend_labels = c(round(seq(0, thresh, length.out = 5))[-5], paste0("Abundance scores,\ntruncated above at ", thresh)),
         #breaks = seq(0, 5, 1),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names]
         )

#p1

Proportional

# proportions:
props <- replace(res$prop_of_nontumor, is.na(res$prop_of_nontumor), 0)

p2<-pheatmap(t(props),
         #scale = "row", 
         cutree_cols = 3,
         cutree_rows = 2,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = round(seq(0, max(props) * 0.99, length.out = 5), 2),
         legend_labels = c(round(seq(0, max(props), length.out = 5), 2)[-5], "Proportion of all\nfitted populations"),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

#p2

Scaled

# scaled abundances:
epsilon <- min(res$beta[res$beta > 0])
mat <- sweep(res$beta, 1, pmax(apply(res$beta, 1, max), epsilon), "/")

pheatmap(t(mat),
         #scale = "row",
         cutree_cols = 3,
         cutree_rows = 3,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = c(round(seq(0, 1, length.out = 5), 2)[-5], 1),
         legend_labels = c(round(seq(0, 1, length.out = 5), 2)[-5], "Scaled abundance\n(ratio to max)"),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

9.4 Barplots

abundance

# define variables to show in heatmaps:
pData(target_Data)$region <- 
    factor(pData(target_Data)$ANN3, unique(target_Data$ANN3))   # Must be manual?
pData(target_Data)$class <- 
    factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))   # Must be manual?

variables_to_plot <- c("slide_name", "ANN1", "ANN3")                     # Must be manual?

#define colors

# only for safeTME colors
#col <- cellcols

#custom celmatrix
#get large number of colors
qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual',]
col_vector = unique(unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals))))
celltypes<-sample(col_vector,length(colnames(res$beta)))
names(celltypes)<-colnames(res$beta)
col<-celltypes


#tempfix for abbreviated and now mismatching annotations
#can just use ann dataframe?
#tmpann<-cbind(ANN1,ANN2,SN)
tmpann <- pData(target_Data)[ann_names]



layout(matrix(c(1, 2, 3, 3), nrow = 2),
       widths = c(10, 3, 10, 3),
       heights = c(1, 8, 10),
      )

par(mar = c(0, 8.2, 0, 0.2))
plot(p1$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))

# data to plot:
mat <- t(res$beta)[, p1$tree_col$order]
# infer scale of negative y-axis for annotation colorbars
ymin <- -max(colSums(mat)) * 0.15
if (!is.finite(ymin)) {
  ymin <- 0
}

# draw barplot:
bp <- barplot(mat,
              cex.lab = 1.5,
              col = col, border = NA,
              cex.names = 1.1,
              las = 2, main = "", ylab = "Abundance scores",
              ylim = c(ymin, max(colSums(mat)))
)


# add color bars for annotations
for (name in rev(variables_to_plot)) {
  yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
  xwidth <- (bp[2] - bp[1]) / 2
  for (i in 1:ncol(mat)) {
    rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
         # border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
         #border = NA, col = ann_colors[[name]][ann[p1$tree_col$order[i], name]]
         border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
    )
  }
}
axis(2,
     at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
     las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)

#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
  legendcols <- c(legendcols, NA, color_list[[name]], NA)
  legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
       pch = 15,
       cex = 1.5,
       col = c(legendcols, rep(NA, 1), rev(col)),
       legend = c(legendnames, "Cell type", rev(names(col))),
)

proportional

# define variables to show in heatmaps:
variables_to_plot <- c("slide_name", "ANN1", "ANN3")

layout(matrix(c(1, 2, 3, 3), nrow = 2),
       widths = c(10, 3, 10, 3),
       heights = c(1, 8, 10),
      )

par(mar = c(0, 8.2, 0, 0.2))
plot(p2$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))

# data to plot:
mat <- t(res$prop_of_nontumor)[, p2$tree_col$order]
  mat <- replace(mat, is.na(mat), 0)
  # infer scale of negative y-axis for annotation colorbars
  ymin <- -0.15

# draw barplot:
bp <- barplot(mat,
              cex.lab = 1.5,
              col = col, border = NA,
              cex.names = 1.1,
              las = 2, main = "", ylab = "Proportion of fitted cells",
              ylim = c(ymin, max(colSums(mat)))
)

# add color bars for annotations
for (name in rev(variables_to_plot)) {
  yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
  xwidth <- (bp[2] - bp[1]) / 2
  for (i in 1:ncol(mat)) {
    rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
         # border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
         #border = NA, col = ann_colors[[name]][ann[p2$tree_col$order[i], name]]
         border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
    )
  }
}
axis(2,
     at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
     las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)


#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
  legendcols <- c(legendcols, NA, color_list[[name]], NA)
  legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
       pch = 15,
       cex = 1.4,
       col = c(legendcols, rep(NA, 1), rev(col)),
       legend = c(legendnames, "Cell type", rev(names(col))),
)

Boxplots

Boxplot per cell from the spatial deconvolution. Choose your annotation in which you want to compare (ANN). The annotation will be divided in conditions and each cell with have a boxplot which include the abundance score of each condition. A statistical test has been added inside the boxplot to check for significance. Feel free to chance the test to fit the data.

significance code p-value *** [0, 0.001] ** (0.001, 0.01] * (0.01, 0.05] . (0.05, 0.1] (0.1, 1]

ANN <- "ANN1" # ANN column with conditions
if (length(unique(target_Data[[ANN]])) > 2) {
  test <- "kruskal.test"
} else {
  test <- "wilcox.test"
}

res_filter <- res[!res@phenoData@data[["beta"]]==0]
res_filter <- as.data.frame(res_filter$beta)
res_filter[res_filter == 0] <- NA
boxplot_prep <- res_filter[ , colSums(is.na(res_filter)) < nrow(res_filter)] 

boxplot_prep$condition <- rownames(boxplot_prep)

for (value in unique(target_Data[[ANN]])) {
  con <- rownames(pData(target_Data)[pData(target_Data)[[ANN]] == value,])
  boxplot_prep$condition[boxplot_prep$condition %in% con] <- value
}

boxplot <- melt(boxplot_prep)
## Using condition as id variables
# function for number of observations 
give.n <- function(x){
  return(data.frame(y = -1, label = paste0("n = ",length(x))))
 #return(c(y = median(x)*1.05, label = length(x))
# experiment with the multiplier to find the perfect position
}

ggplot(boxplot, aes(factor(condition), value, fill = condition)) + 
  geom_boxplot() + 
  geom_jitter(position=position_jitter(w=0.1,h=0.1)) +
  facet_wrap(~variable, scale="free", ncol = 5) +
  theme(text = element_text(size = 20)) +
  rotate_x_text(angle = 45) +
  labs(
    title = "Spatial Deconvolution",
    x = "Conditions",
    y = "Abuncance score") + 
  stat_compare_means(method = test, label.y = -3, size=5) + 
  stat_compare_means(label = "p.signif", method = "t.test",
                     ref.group = ".all.", hide.ns = TRUE, size=5) +
  stat_summary(fun.data = give.n, geom = "text", size = 5)

#stat_compare_means(size=5)

11 CNV

Copy Number Variation analysis with the R software package inferCNV (https://github.com/broadinstitute/inferCNV/wiki).

InferCNV is used to explore tumor single cell RNA-Seq data to identify evidence for somatic large-scale chromosomal copy number alterations, such as gains or deletions of entire chromosomes or large segments of chromosomes. This is done by exploring expression intensity of genes across positions of tumor genome in comparison to a set of reference ‘normal’ cells. A heatmap is generated illustrating the relative expression intensities across each chromosome, and it often becomes readily apparent as to which regions of the tumor genome are over-abundant or less-abundant as compared to that of normal cells.

Per patient a cnv analysis with a provided reference group. The gene order is made from the projects .pkc file.

Select the annotation where the CNV analysis should look at specifically as group and specify a subgroup as group_filter if the group of interest is a subgroup inside the annotation. For example if the CNV analysis needs to be only on tumor regions the group would be ANNX(ANN column with the info about the tumor regions) and group_filter PanCK+. The annotation that references the patients as patients. If there are no patients leave the parameter empty as ““. The annotation that should be included in the results including a reference set as cnv_target. This will create patient specific files with all the information inferCNV needs.

Select the reference set in reference. This can be more than one. Every patient without its own reference will not be analysed.

#########PARAMETERS########
reference <- c("normal")
###########################

failed <- c()
cnv_list <- list()

for (patient in patient_list) {
  # Name output folder
  out_dir <- paste0(patient, "_CNV")
  if (str_detect(paste(readLines(paste0(patient, ".Annotations.tsv")), collapse = ''), reference) == FALSE) {
    failed <- c(failed, patient)
    next
    }

  # Create the infercnv object
  infercnv_obj = CreateInfercnvObject(raw_counts_matrix= paste0(patient, ".Counts.tsv"),
                                      annotations_file= paste0(patient, ".Annotations.tsv"),
                                      delim="\t",
                                      gene_order_file= "gene_order_Hs_WTA_v1_pkc.txt",
                                      #gene_order_file= "gencode_v19_gene_pos.txt",
                                      ref_group_names= reference, # input the normal/reference group names
                                      chr_exclude = c("chrM"))#c("chrM")) # Default excludes chrX, chrY and chrM. By only picking chrM you include the X and Y chromosomes.

  # perform infercnv operations to reveal cnv signal. For all options: https://rdrr.io/github/broadinstitute/infercnv/man/run.html
  #cnv_list[[patient]]
  infercnv_obj <- infercnv::run(infercnv_obj,
                               cutoff=0.1,  # use 1 for smart-seq, 0.1 for 10x-genomics
                               out_dir= out_dir,  # dir is auto-created for storing outputs
                               cluster_by_groups=FALSE,   # cluster
                               denoise=TRUE,
                               HMM=FALSE,
                               tumor_subcluster_partition_method = c("qnorm"),
                               analysis_mode = "subclusters",
                               no_plot=TRUE
                               #,debug=TRUE
                               )

  plot_cnv(infercnv_obj,
          out_dir = paste0(patient, "_CNV"),
          title = "inferCNV",
          obs_title = "Observations (Cells)",
          ref_title = "References (Cells)",
          cluster_by_groups = FALSE,
          cluster_references = FALSE,
          plot_chr_scale = FALSE,
          #chr_lengths = NULL,
          k_obs_groups = 1,
          contig_cex = 1.5,
          #x.center = mean(infercnv_obj@expr.data),
          x.range = "auto",
          #hclust_method = "ward.D",
          output_filename = "infercnv",
          output_format = "png",
          png_res = 300
          )
}
for (patient in patient_list) {
  print(paste(group_filter, patient))
  if (patient %in% failed) {
    print(" ^    Patient contained no reference group")
    next
  }
  img <- readPNG(paste(paste0(patient, "_CNV"), "/infercnv.png", sep = ""))
  grid::grid.newpage()
  grid::grid.raster(img)
}
## [1] " dummy"

11.1 Dendrogram

Closer look at the dendrogram from the CNV analysis per patient where the nodes as written numbers. Use the numbers to select subgroups for further analysis in ‘t-test on two subgroups’.

#out_dir <- "T1_NANO_012_CNV"
trees <- list()

for (patient in patient_list) {
  if (patient %in% failed) {
    #print("Patient contained no reference group")
    next
  }
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
obv <- read.csv(paste(patient,"_CNV/infercnv.observation_groupings.txt", sep = ""), sep="")
trees[[patient]] <- ggtree(
  tree, ladderize=F) +
  geom_treescale() +
  geom_tiplab(color=obv$Annotation.Color, hjust=-.2) +
  coord_cartesian(clip = 'off') +
  theme_tree2(plot.margin=margin(6, 200, 6, 6)) +
  geom_text2(aes(label=node), hjust=-.3, size = 3) +
  ggplot2::labs(title = patient)
}

grid.arrange(grobs=trees,ncol=3)

t-test chr with groups

Compare the contrast within a chromosome. Select a chromosome, contrast, and patient of interest to run a t-test on.

#########PARAMETERS########
chr <- "chrX" # Select chromosome of interest
contrast <- c("normal", "DKD") # Select contrast
patient <- "dummy"
###########################

positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end")
select_genes <- positions$gene[positions$chr == chr] # Grab chr specific genes

annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation

filter_counts <- as.data.frame(target_Data@assayData[["log_q"]])
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # filter out samples that are not the interesting region

select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # filter out non chromosome specific genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1

log_q_filter <-as.data.frame(filter_counts)

comps_df<-data.frame(comp='',val='')

for (active_group1 in contrast) {
    for (active_group2 in contrast) {

      #supress reduncant compares
      if(active_group1==active_group2) {next}
      comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
      #print(comp)
      if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)

      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
      group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]

      #run t_tests
      results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"

      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)

      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)

      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
      colnames(fchanges)<-"FC"
      results<-cbind(results,fchanges)

      #add genenames
      results$Gene<-rownames(results)

      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 0.5"
      results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))

      #vulcanoplot

      # pick top genes for either side of volcano to label
      # order genes for convenience:

      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[ind, 'Gene'][
                   order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
                 results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix

      # Graph results
      plots[[counter]]<- ggplot(results,
                                      aes(x = FC, y = -log10(adj_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched genes in", chr, "-", active_group2," <- log2(FC) -> Enriched in", active_group1),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 20) +
        theme(legend.position = "bottom") +
        #ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
        ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))

      #store tables for display later
      tables[[counter]]<-results

      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
    }
  }

grid.arrange(grobs=plots,ncol=2)

t-test on two subgroups

Select nodes from the dendrogram as subgroups, in this case nodes 18 and 15. Decide on the involved chromosomes and add them into the chr_list. Specify the patient in patient.

#########PARAMETERS########
chr_list <- c("chr22") # Choose needed chromosomes of the target area. Think of it as the x-axis
patient <- "dummy"
node_interest <- 233 # Choose subgroup 1
node_interest2 <- 216 # Choose subgroup 2
###########################

tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
tree_info <- tree %>% as.treedata %>% as_tibble # transform tree into accessible data

samples_interest <- offspring(tree_info, node_interest) # Get labels attached to the group
samples_interest <- as.character(na.omit(samples_interest$label)) # formatting
paste("Subgroup 1: ", samples_interest)
##  [1] "Subgroup 1:  DSP-1001250007864-D-H08"
##  [2] "Subgroup 1:  DSP-1001250007864-D-H10"
##  [3] "Subgroup 1:  DSP-1001250007864-D-H06"
##  [4] "Subgroup 1:  DSP-1001250007864-D-H02"
##  [5] "Subgroup 1:  DSP-1001250007864-D-H04"
##  [6] "Subgroup 1:  DSP-1001250007864-D-H11"
##  [7] "Subgroup 1:  DSP-1001250007864-D-H12"
##  [8] "Subgroup 1:  DSP-1001250007864-D-H05"
##  [9] "Subgroup 1:  DSP-1001250007864-D-H07"
## [10] "Subgroup 1:  DSP-1001250007864-D-H09"
## [11] "Subgroup 1:  DSP-1001250007864-D-H01"
## [12] "Subgroup 1:  DSP-1001250007864-D-H03"
## [13] "Subgroup 1:  DSP-1001250007851-H-A09"
## [14] "Subgroup 1:  DSP-1001250007851-H-C04"
## [15] "Subgroup 1:  DSP-1001250007851-H-D06"
## [16] "Subgroup 1:  DSP-1001250007851-H-C07"
## [17] "Subgroup 1:  DSP-1001250007851-H-D12"
## [18] "Subgroup 1:  DSP-1001250007851-H-A12"
## [19] "Subgroup 1:  DSP-1001250007851-H-A03"
## [20] "Subgroup 1:  DSP-1001250007851-H-D09"
## [21] "Subgroup 1:  DSP-1001250007851-H-A04"
## [22] "Subgroup 1:  DSP-1001250007851-H-D10"
## [23] "Subgroup 1:  DSP-1001250007851-H-A10"
## [24] "Subgroup 1:  DSP-1001250007851-H-C01"
## [25] "Subgroup 1:  DSP-1001250007851-H-C03"
## [26] "Subgroup 1:  DSP-1001250007851-H-A05"
## [27] "Subgroup 1:  DSP-1001250007851-H-C02"
## [28] "Subgroup 1:  DSP-1001250007851-H-B12"
## [29] "Subgroup 1:  DSP-1001250007851-H-B06"
## [30] "Subgroup 1:  DSP-1001250007851-H-B11"
## [31] "Subgroup 1:  DSP-1001250007851-H-B04"
## [32] "Subgroup 1:  DSP-1001250007851-H-B05"
## [33] "Subgroup 1:  DSP-1001250007851-H-D11"
## [34] "Subgroup 1:  DSP-1001250007851-H-D05"
## [35] "Subgroup 1:  DSP-1001250007851-H-B07"
## [36] "Subgroup 1:  DSP-1001250007851-H-C08"
## [37] "Subgroup 1:  DSP-1001250007851-H-D08"
## [38] "Subgroup 1:  DSP-1001250007851-H-B10"
## [39] "Subgroup 1:  DSP-1001250007851-H-A02"
## [40] "Subgroup 1:  DSP-1001250007851-H-A08"
## [41] "Subgroup 1:  DSP-1001250007851-H-A06"
## [42] "Subgroup 1:  DSP-1001250007851-H-A11"
## [43] "Subgroup 1:  DSP-1001250007851-H-D04"
## [44] "Subgroup 1:  DSP-1001250007851-H-D07"
## [45] "Subgroup 1:  DSP-1001250007851-H-B01"
## [46] "Subgroup 1:  DSP-1001250007851-H-B08"
## [47] "Subgroup 1:  DSP-1001250007851-H-A07"
## [48] "Subgroup 1:  DSP-1001250007851-H-B02"
## [49] "Subgroup 1:  DSP-1001250007851-H-B03"
## [50] "Subgroup 1:  DSP-1001250007851-H-C06"
## [51] "Subgroup 1:  DSP-1001250007851-H-D01"
## [52] "Subgroup 1:  DSP-1001250007851-H-C12"
## [53] "Subgroup 1:  DSP-1001250007851-H-C09"
## [54] "Subgroup 1:  DSP-1001250007851-H-C05"
## [55] "Subgroup 1:  DSP-1001250007851-H-C11"
## [56] "Subgroup 1:  DSP-1001250007851-H-C10"
## [57] "Subgroup 1:  DSP-1001250007851-H-B09"
## [58] "Subgroup 1:  DSP-1001250007851-H-D03"
## [59] "Subgroup 1:  DSP-1001250007851-H-D02"
## [60] "Subgroup 1:  DSP-1001250007868-B-A02"
samples_interest2 <- offspring(tree_info, node_interest2)
samples_interest2 <- as.character(na.omit(samples_interest2$label))
paste("Subgroup 2: ", samples_interest2)
##  [1] "Subgroup 2:  DSP-1001250007851-H-F10"
##  [2] "Subgroup 2:  DSP-1001250007851-H-F04"
##  [3] "Subgroup 2:  DSP-1001250007851-H-F11"
##  [4] "Subgroup 2:  DSP-1001250007851-H-F01"
##  [5] "Subgroup 2:  DSP-1001250007851-H-F05"
##  [6] "Subgroup 2:  DSP-1001250007851-H-E08"
##  [7] "Subgroup 2:  DSP-1001250007851-H-F03"
##  [8] "Subgroup 2:  DSP-1001250007851-H-F02"
##  [9] "Subgroup 2:  DSP-1001250007851-H-F09"
## [10] "Subgroup 2:  DSP-1001250007851-H-F06"
## [11] "Subgroup 2:  DSP-1001250007851-H-F07"
## [12] "Subgroup 2:  DSP-1001250007868-B-A05"
## [13] "Subgroup 2:  DSP-1001250007868-B-A03"
## [14] "Subgroup 2:  DSP-1001250007868-B-B01"
## [15] "Subgroup 2:  DSP-1001250007851-H-F12"
## [16] "Subgroup 2:  DSP-1001250007868-B-A04"
## [17] "Subgroup 2:  DSP-1002510007866-C-G02"
## [18] "Subgroup 2:  DSP-1002510007866-C-H12"
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end") # Formatting

select_genes <- positions$gene[positions$chr %in% chr_list] # | positions$chr == chr2] # Grab chromosome specific genes

annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation[annotation$Sample_ID %in% samples_interest | annotation$Sample_ID %in% samples_interest2,] # Grab subgroup specific Sample IDs

filter_counts <- target_Data@assayData[["log_q"]]
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # Filter Sample ID's

select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # Filter genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1

log_q_filter <-as.data.frame(filter_counts)

comps_df<-data.frame(comp='',val='')


active_group1 <- samples_interest #subgroup 1
active_group2 <- samples_interest2 #subgroup 2

# for (active_group1 in c("sub1")) {
#     for (active_group2 in c("sub2")) {

      #supress reduncant compares
      #if(active_group1==active_group2) {next}
      #comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
      #print(comp)
      #if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)

      # labels[[counter]]<-paste(active_group1," vs ", active_group2)
      # group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
      # group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]

      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group1]]
      group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group2]]

      #run t_tests
      results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"

      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)

      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)

      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
      colnames(fchanges)<-"FC"
      results<-cbind(results,fchanges)

      #add genenames
      results$Gene<-rownames(results)

      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 0.5"
      results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))

      #vulcanoplot

      # pick top genes for either side of volcano to label
      # order genes for convenience:

      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[ind, 'Gene'][
                   order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
                 results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix

      # Graph results
      #plots[[counter]]<- ggplot(results,
      p <- ggplot(results,
                                      aes(x = FC, y = -log10(adj_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched genes from", chr_list, "in", "subgroup 2"," <- log2(FC) -> Enriched in", "subgroup 1"),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 20) +
        theme(legend.position = "bottom") +
        #ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
        ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))

      #store tables for display later
      tables[[counter]]<-results

      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
  #   }
  # }

#ggplotly(p)
p

#grid.arrange(grobs=plots,ncol=2)

12 Code & Versions

Pipelineversion: v1 based on: https://bioconductor.org/packages/devel/workflows/vignettes/GeoMxWorkflows/inst/doc/GeomxTools_RNA-NGS_Analysis.html

The underlying code can be downloaded from the ‘Code’, button on the top of this page. Choose option ‘download Rmd’ to download the full pipeline which can be opened in R or Rstudio. Some filepaths are hardcoded and need to be changed according to your setup.

12.1 R session information

sessionInfo()
## R version 4.2.1 (2022-06-23 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19044)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=English_Netherlands.utf8  LC_CTYPE=English_Netherlands.utf8   
## [3] LC_MONETARY=English_Netherlands.utf8 LC_NUMERIC=C                        
## [5] LC_TIME=English_Netherlands.utf8    
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] ggpubr_0.6.0                tidytree_0.4.2             
##  [3] ggtree_3.6.2                Biostrings_2.66.0          
##  [5] XVector_0.38.0              ape_5.6-2                  
##  [7] infercnv_1.14.0             preprocessCore_1.60.1      
##  [9] sva_3.46.0                  BiocParallel_1.32.1        
## [11] genefilter_1.80.1           mgcv_1.8-40                
## [13] nlme_3.1-157                scater_1.26.1              
## [15] scuttle_1.8.1               ggalluvial_0.12.3          
## [17] limma_3.54.0                SpatialExperiment_1.8.0    
## [19] SingleCellExperiment_1.20.0 SummarizedExperiment_1.28.0
## [21] GenomicRanges_1.50.1        GenomeInfoDb_1.34.3        
## [23] IRanges_2.32.0              MatrixGenerics_1.10.0      
## [25] matrixStats_0.63.0          standR_1.3.1               
## [27] viridis_0.6.2               viridisLite_0.4.1          
## [29] readxl_1.4.1                png_0.1-7                  
## [31] pathview_1.38.0             fgsea_1.24.0               
## [33] clusterProfiler_4.6.2       lubridate_1.9.2            
## [35] forcats_1.0.0               stringr_1.5.0              
## [37] purrr_1.0.1                 readr_2.1.4                
## [39] tidyr_1.3.0                 tibble_3.1.8               
## [41] tidyverse_2.0.0             gt_0.8.0                   
## [43] SpatialOmicsOverlay_0.99.11 RColorBrewer_1.1-3         
## [45] gridExtra_2.3               plotly_4.10.1              
## [47] DT_0.26                     ggrepel_0.9.2              
## [49] pheatmap_1.0.12             Rtsne_0.16                 
## [51] umap_0.2.9.0                cowplot_1.1.1              
## [53] reshape2_1.4.4              scales_1.2.1               
## [55] ggforce_0.4.1               dplyr_1.1.0                
## [57] knitr_1.41                  msigdbr_7.5.1              
## [59] GSVA_1.46.0                 SpatialDecon_1.8.0         
## [61] GeoMxWorkflows_1.5.0        GeomxTools_3.2.0           
## [63] NanoStringNCTools_1.6.0     ggplot2_3.4.1              
## [65] S4Vectors_0.36.0            Biobase_2.58.0             
## [67] BiocGenerics_0.44.0        
## 
## loaded via a namespace (and not attached):
##   [1] graphlayouts_0.8.3        pbapply_1.6-0            
##   [3] lattice_0.20-45           rJava_1.0-6              
##   [5] vctrs_0.6.0               blob_1.2.3               
##   [7] survival_3.3-1            spatstat.data_3.0-0      
##   [9] later_1.3.0               nloptr_2.0.3             
##  [11] DBI_1.1.3                 R.utils_2.12.2           
##  [13] rappdirs_0.3.3            uwot_0.1.14              
##  [15] dqrng_0.3.0               jpeg_0.1-9               
##  [17] zlibbioc_1.44.0           htmlwidgets_1.5.4        
##  [19] mvtnorm_1.1-3             future_1.29.0            
##  [21] leiden_0.4.3              logNormReg_0.5-0         
##  [23] parallel_4.2.1            irlba_2.3.5.1            
##  [25] tidygraph_1.2.2           Rcpp_1.0.9               
##  [27] KernSmooth_2.23-20        promises_1.2.0.1         
##  [29] DelayedArray_0.24.0       RcppParallel_5.1.5       
##  [31] magick_2.7.3              graph_1.76.0             
##  [33] RSpectra_0.16-1           fastmatch_1.1-3          
##  [35] rjags_4-13                digest_0.6.30            
##  [37] sctransform_0.3.5         scatterpie_0.1.8         
##  [39] DOSE_3.24.2               ggraph_2.1.0             
##  [41] pkgconfig_2.0.3           GO.db_3.16.0             
##  [43] spatstat.random_3.0-1     DelayedMatrixStats_1.20.0
##  [45] ggbeeswarm_0.6.0          iterators_1.0.14         
##  [47] minqa_1.2.5               DropletUtils_1.18.0      
##  [49] reticulate_1.26           beeswarm_0.4.0           
##  [51] modeltools_0.2-23         xfun_0.35                
##  [53] bslib_0.4.1               zoo_1.8-11               
##  [55] tidyselect_1.2.0          ica_1.0-3                
##  [57] gson_0.1.0                snow_0.4-4               
##  [59] rlang_1.1.0               jquerylib_0.1.4          
##  [61] glue_1.6.2                EBImage_4.40.0           
##  [63] lambda.r_1.2.4            ggsignif_0.6.4           
##  [65] labeling_0.4.2            GGally_2.1.2             
##  [67] httpuv_1.6.6              repmis_0.5               
##  [69] BiocNeighbors_1.16.0      TH.data_1.1-1            
##  [71] annotate_1.76.0           jsonlite_1.8.3           
##  [73] bit_4.0.5                 mime_0.12                
##  [75] systemfonts_1.0.4         gplots_3.1.3             
##  [77] BiocStyle_2.26.0          stringi_1.7.8            
##  [79] spatstat.sparse_3.0-0     scattermore_0.8          
##  [81] spatstat.explore_3.0-5    yulab.utils_0.0.6        
##  [83] bitops_1.0-7              cli_3.4.1                
##  [85] rhdf5filters_1.10.0       RSQLite_2.2.18           
##  [87] libcoin_1.0-9             data.table_1.14.6        
##  [89] KEGGgraph_1.58.0          timechange_0.1.1         
##  [91] rstudioapi_0.14           fftwtools_0.9-11         
##  [93] qvalue_2.30.0             fastcluster_1.2.3        
##  [95] locfit_1.5-9.6            listenv_0.8.0            
##  [97] ggthemes_4.2.4            miniUI_0.1.1.1           
##  [99] gridGraphics_0.5-1        R.oo_1.25.0              
## [101] dbplyr_2.3.1              lifecycle_1.0.3          
## [103] munsell_0.5.0             cellranger_1.1.0         
## [105] R.methodsS3_1.8.2         caTools_1.18.2           
## [107] codetools_0.2-18          coda_0.19-4              
## [109] vipor_0.4.5               lmtest_0.9-40            
## [111] xtable_1.8-4              ROCR_1.0-11              
## [113] formatR_1.14              BiocManager_1.30.19      
## [115] abind_1.4-5               farver_2.1.1             
## [117] parallelly_1.32.1         RANN_2.6.1               
## [119] aplot_0.1.10              askpass_1.1              
## [121] tiff_0.1-11               parallelDist_0.2.6       
## [123] SeuratObject_4.1.3        RcppAnnoy_0.0.20         
## [125] goftest_1.2-3             patchwork_1.1.2          
## [127] futile.options_1.0.1      cluster_2.1.3            
## [129] future.apply_1.10.0       Seurat_4.3.0             
## [131] Matrix_1.5-3              ellipsis_0.3.2           
## [133] ggridges_0.5.4            igraph_1.3.5             
## [135] lmerTest_3.1-3            argparse_2.2.1           
## [137] spatstat.utils_3.0-1      htmltools_0.5.3          
## [139] BiocFileCache_2.6.0       yaml_2.3.6               
## [141] utf8_1.2.2                XML_3.99-0.12            
## [143] withr_2.5.0               fitdistrplus_1.1-8       
## [145] bit64_4.0.5               multcomp_1.4-20          
## [147] foreach_1.5.2             outliers_0.15            
## [149] progressr_0.11.0          GOSemSim_2.24.0          
## [151] rsvd_1.0.5                ScaledMatrix_1.6.0       
## [153] memoise_2.0.1             evaluate_0.18            
## [155] tzdb_0.3.0                curl_4.3.3               
## [157] fansi_1.0.3               highr_0.9                
## [159] ggiraph_0.8.4             GSEABase_1.60.0          
## [161] tensor_1.5                edgeR_3.40.0             
## [163] cachem_1.0.6              org.Hs.eg.db_3.16.0      
## [165] deldir_1.0-6              HDO.db_0.99.1            
## [167] babelgene_22.9            rjson_0.2.21             
## [169] rstatix_0.7.2             tools_4.2.1              
## [171] sass_0.4.3                sandwich_3.0-2           
## [173] magrittr_2.0.3            RCurl_1.98-1.9           
## [175] car_3.1-1                 phyclust_0.1-32          
## [177] ggplotify_0.1.0           xml2_1.3.3               
## [179] httr_1.4.5                RBioFormats_0.99.9       
## [181] rmarkdown_2.18            boot_1.3-28              
## [183] globals_0.16.2            R6_2.5.1                 
## [185] Rhdf5lib_1.20.0           KEGGREST_1.38.0          
## [187] treeio_1.22.0             gtools_3.9.4             
## [189] coin_1.4-2                beachmat_2.14.0          
## [191] HDF5Array_1.26.0          BiocSingular_1.14.0      
## [193] rhdf5_2.42.0              splines_4.2.1            
## [195] carData_3.0-5             ggfun_0.0.9              
## [197] colorspace_2.0-3          generics_0.1.3           
## [199] base64enc_0.1-3           gridtext_0.1.5           
## [201] pillar_1.8.1              Rgraphviz_2.42.0         
## [203] tweenr_2.0.2              sp_1.5-1                 
## [205] uuid_1.1-0                R.cache_0.16.0           
## [207] GenomeInfoDbData_1.2.9    plyr_1.8.8               
## [209] gtable_0.3.1              futile.logger_1.4.3      
## [211] shadowtext_0.1.2          fastmap_1.1.0            
## [213] EnvStats_2.7.0            crosstalk_1.2.0          
## [215] doParallel_1.0.17         AnnotationDbi_1.60.0     
## [217] broom_1.0.4               backports_1.4.1          
## [219] openssl_2.0.4             filelock_1.0.2           
## [221] plotrix_3.8-2             lme4_1.1-31              
## [223] enrichplot_1.18.3         hms_1.1.2                
## [225] shiny_1.7.3               polyclip_1.10-4          
## [227] grid_4.2.1                numDeriv_2016.8-1.1      
## [229] lazyeval_0.2.2            crayon_1.5.2             
## [231] MASS_7.3-57               downloader_0.4           
## [233] sparseMatrixStats_1.10.0  reshape_0.8.9            
## [235] compiler_4.2.1            ggtext_0.1.2             
## [237] spatstat.geom_3.0-3

12.2 References

knitr::knit_exit()
LS0tDQp0aXRsZTogIk5hbm9zdHJpbmcgR2VvTXggYW5hbHlzaXMiDQphdXRob3I6ICJJZXMgTmlqbWFuICYgUGltIEtsb29zdGVybWFuIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL1VTRVEtbG9nby1zdWJ0aXRsZS5wbmcpDQoNCiMgS2xhbnQ6IFVzZXENCg0KIyBQcm9qZWN0OiBTcGF0aWFsIE9yZ2FuIEF0bGFzDQoNCiMgRGF0YXNldDogS2lkbmV5DQoNCioqZGF0ZTogYHIgZm9ybWF0KFN5cy50aW1lKCksICclSCAlTSAlYSAlZCAlQiwgJVknKWAqKg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvZGVjb3JhdGlvbi1zdHJva2UtZmxhdC5wbmcpDQoNCioqbG9hZGluZyBkZXBlbmRlbmNpZXMqKiBQbGVhc2UgbWFrZSBzdXJlIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgYXJlDQppbnN0YWxsZWQgYW5kIHJlcXVpcmVkIGxpYnJhcmllcyBjYW4gYmUgbG9hZGVkOg0KDQotICAgaW5zdGFsbC5wYWNrYWdlcygicGtnYnVpbGQiKSAvLyBwa2didWlsZDo6Y2hlY2tfYnVpbGRfdG9vbHMoKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCi0gICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIk5hbm9zdHJpbmctQmlvc3RhdHMvTmFub1N0cmluZ05DVG9vbHMiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiTmFub3N0cmluZy1CaW9zdGF0cy9HZW9teFRvb2xzIiwgcmVmID0NCiAgICAiZGV2IikNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR2VvTXhXb3JrZmxvd3MiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlNwYXRpYWxEZWNvbiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdTVkEiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygiRFQiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIm1zaWdkYnIiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRpZ2VzdCIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZSIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkVCSW1hZ2UiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ3NjYXR0ZXJtb3JlJykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwYmFwcGx5JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwbG90cml4JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdnZ3RleHQnKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ1JCaW9Gb3JtYXRzJykNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiYW9sZXMvUkJpb0Zvcm1hdHMiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIkM6L1VzZXJzL2luaWptYW4vRG93bmxvYWRzL1NwYXRpYWxPbWljc092ZXJsYXktMC45OS4xMy1iZXRhLnRhci5neiIsDQogICAgZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSBOVUxMKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoImNsdXN0ZXJQcm9maWxlciIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoInBhdGh2aWV3IikNCg0KYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNsb2FkIGxpYnJhcmllcw0KDQojc3RhY2tfc2l6ZSA8LSBnZXRPcHRpb24oInBhbmRvYy5zdGFjay5zaXplIiwgZGVmYXVsdCA9ICI1MTJtIikNCiNvcHRpb25zKGphdmEucGFyYW1ldGVycyA9IGMoIi1YWDorVXNlQ29uY01hcmtTd2VlcEdDIiwgIi1YbXg4MTkybSwgLVhYOk1ldGFzcGFjZVNpemU9MTAyNE0iKSkNCm9wdGlvbnMoamF2YS5wYXJhbWV0ZXJzID0gYygiLVhYOitVc2VDb25jTWFya1N3ZWVwR0MiLCAiLVhteDgxOTJtIikpDQpsaWJyYXJ5KE5hbm9TdHJpbmdOQ1Rvb2xzKQ0KbGlicmFyeShHZW9teFRvb2xzKQ0KbGlicmFyeShHZW9NeFdvcmtmbG93cykNCmxpYnJhcnkoU3BhdGlhbERlY29uKQ0KbGlicmFyeShHU1ZBKSAjZm9yIHBhdGh3YXkgYW5hbHlzZXMNCmxpYnJhcnkobXNpZ2RicikgI2ZvciBtb2xlY3VsYXIgc2lnbmF0dXJlcyBpbiBwYXRod2F5IGFuYWx5c2VzDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dmb3JjZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc2NhbGVzKSAjIGZvciBwZXJjZW50DQpsaWJyYXJ5KHJlc2hhcGUyKSAgIyBmb3IgbWVsdA0KbGlicmFyeShjb3dwbG90KSAgICMgZm9yIHBsb3RfZ3JpZA0KbGlicmFyeSh1bWFwKQ0KbGlicmFyeShSdHNuZSkNCmxpYnJhcnkocGhlYXRtYXApICAjIGZvciBwaGVhdG1hcA0KbGlicmFyeShnZ3JlcGVsKSANCmxpYnJhcnkoc2NhbGVzKSAjZm9yIGdncGxvdCBwZWF1ZG9sb2cgdG8gcHJldmVudCBlcnJvcnMgb24gbG9nKDApDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShTcGF0aWFsT21pY3NPdmVybGF5KQ0KbGlicmFyeShndCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KGZnc2VhKQ0KbGlicmFyeShwYXRodmlldykNCmxpYnJhcnkocG5nKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmxpYnJhcnkoc3RhbmRSKQ0KbGlicmFyeShTcGF0aWFsRXhwZXJpbWVudCkNCmxpYnJhcnkobGltbWEpDQpsaWJyYXJ5KGdnYWxsdXZpYWwpDQpsaWJyYXJ5KHNjYXRlcikNCg0KQmlvY01hbmFnZXI6Omluc3RhbGwoInN2YSIpDQpsaWJyYXJ5KHN2YSkNCg0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJwcmVwcm9jZXNzQ29yZSIpICNxdWFudGlsZSBub3JtDQpsaWJyYXJ5KHByZXByb2Nlc3NDb3JlKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiUG9seWNocm9tZSIpICNHZXQgY29sb3JzDQojbGlicmFyeShQb2x5Y2hyb21lKQ0KDQoNCmxpYnJhcnkoaW5mZXJjbnYpICMgQ05WDQpsaWJyYXJ5KCJhcGUiKQ0KbGlicmFyeSgiQmlvc3RyaW5ncyIpDQpsaWJyYXJ5KCJnZ3RyZWUiKQ0KbGlicmFyeSh0aWR5dHJlZSkNCmxpYnJhcnkoZ2dwdWJyKQ0KYGBgDQoNCiMgMSBsb2FkaW5nIGJhc2UgZmlsZXMNCg0KYGBge3IgbG9hZGluZ19iYXNlX2RhdGF9DQojIFJlZmVyZW5jZSB0aGUgbWFpbiBmb2xkZXIgJ2ZpbGUucGF0aCcgY29udGFpbmluZyB0aGUgc3ViLWZvbGRlcnMgd2l0aCBlYWNoIGRhdGEgZmlsZSB0eXBlOg0KZGF0YWRpcjwtZmlsZS5wYXRoKCJMOi9wa2xvb3N0ZXJtYW4vR2l0aHViL0RLRF9LaWRuZXkvIikNCmBgYA0KDQpUbyBsb2NhdGUgYSBzcGVjaWZpYyBmaWxlIHBhdGggcmVwbGFjZSB0aGUgYWJvdmUgbGluZSB3aXRoIGRhdGFkaXIgXDwtDQpmaWxlLnBhdGgoIlx+L0ZvbGRlci9TdWJGb2xkZXIvRGF0YUxvY2F0aW9uIikgcmVwbGFjZSB0aGUgRm9sZGVyLA0KU3ViRm9sZGVyLCBEYXRhTG9jYXRpb24gYXMgbmVlZGVkLiBUaGUgRGF0YUxvY2F0aW9uIGZvbGRlciBzaG91bGQNCmNvbnRhaW4gYSBkY2NzLCBwa2NzLCBhbmQgYW5ub3RhdGlvbiBmb2xkZXIgd2l0aCBlYWNoIHNldCBvZiBmaWxlcw0KcHJlc2VudCBhcyBuZWVkZWQgYXV0b21hdGljYWxseSBsaXN0IGZpbGVzIGluIGVhY2ggZGlyZWN0b3J5IGZvciB1c2UuDQoNCioqVGFrZSBjYXJlIHlvdSBpbXBvcnQgYSBjb2x1bW4gd2l0aCBudWNsZWkgY291bnQgc2VwYXJhdGVseSBpZiB5b3UNCndhbnQuKioNCg0KYGBge3IgcGFyc2VfZmlsZXN9DQpEQ0NGaWxlcyA8LSBkaXIoZmlsZS5wYXRoKGRhdGFkaXIsICJkY2NzIiksIHBhdHRlcm4gPSAiLmRjYyQiLA0KICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFLCByZWN1cnNpdmUgPSBUUlVFKQ0KUEtDRmlsZXMgPC0gZGlyKGZpbGUucGF0aChkYXRhZGlyLCAicGtjcyIpLCBwYXR0ZXJuID0gIi5wa2MkIiwNCiAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNClNhbXBsZUFubm90YXRpb25GaWxlIDwtDQogIGRpcihmaWxlLnBhdGgoZGF0YWRpciwgImFubm90YXRpb24iKSwgcGF0dGVybiA9ICJeW15+XSIsDQogICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNCmBgYA0KDQojIDIgbG9hZCBkYXRhDQoNCmBgYHtyIGxvYWRfZGF0YX0NCkRhdGEgPC0NCiAgcmVhZE5hbm9TdHJpbmdHZW9NeFNldChkY2NGaWxlcyA9IERDQ0ZpbGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBrY0ZpbGVzID0gUEtDRmlsZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhRmlsZSA9IFNhbXBsZUFubm90YXRpb25GaWxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YVNoZWV0ID0gIlRlbXBsYXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFEY2NDb2xOYW1lID0gIlNhbXBsZV9JRCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgcHJvdG9jb2xEYXRhQ29sTmFtZXMgPSBjKCJhb2kiLCAicm9pIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgZXhwZXJpbWVudERhdGFDb2xOYW1lcyA9IGMoInBhbmVsIikpDQoNCiNzYXZlIGRhdGEgdG8gcHJldmVudCBsb2FkaW5nIHRpbWUgZm9yIHJldGFrZXMNCiNzYXZlRGF0YTwtRGF0YQ0KI0RhdGE8LXNhdmVEYXRhDQoNCiNjaGFuZ2UgRGF0YSBjb2x1bW4gbmFtZXMgYW5kIG1hbnVhbCBjb3JyZWN0aW9uIG9mIHNwZWxsaW5nIGVycm9ycw0KRGF0YUBwaGVub0RhdGFAZGF0YVtbInNsaWRlX25hbWUiXV08LURhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZSBuYW1lIl1dDQpEYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGUgbmFtZSJdXTwtICBOVUxMDQoNCg0KIysxIHJlZmVyZW5jZXMgdGhlIHNsaWRlIG5hbWUgY29sdW1uDQphbm5fc2l6ZTwtbGVuZ3RoKGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpW2dyZXBsKCJBTk4iLGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpKV0pKzEgDQphbm5fbmFtZXM8LWMoY29sbmFtZXMoRGF0YUBwaGVub0RhdGFAZGF0YSlbZ3JlcGwoIkFOTiIsY29sbmFtZXMoRGF0YUBwaGVub0RhdGFAZGF0YSkpXSwic2xpZGVfbmFtZSIpDQoNCiMgRmVlbCBmcmVlIHRvIGNoYW5nZSB0aGUgb3JkZXIgb2Ygd2hpY2ggY29sb3JzIGFyZSBhcHBvaW50ZWQuDQpjb2xvcjwtYygiI0EzNDlBNCIsICIjRkZGRjMzIiwgIiNFNzI5OEEiLCAiIzA5MTgzMyIsICIjMUI5RTc3IiwgIiNEOTVGMDIiLCAiIzc1NzBCMyIsICAiIzY2QTYxRSIsICIjRTZBQjAyIiwgIiM4REQzQzciLCAiIzlGMDAwRiIsICIjQkVCQURBIiwgIiNGQjgwNzIiLCAiIzgwQjFEMyIsICIjRkRCNDYyIiwgIiNCM0RFNjkiLCAiI0ZDQ0RFNSIsICIjRDlEOUQ5IiwgIiNCQzgwQkQiLCAiI0NDRUJDNSIsICIjRkZFRDZGIiwgIiMzNzdFQjgiLCAiIzk4NEVBMyIsICIjNERBRjRBIiwgIiNGRjcxQ0UiLCAiI0ZGN0YwMCIsICIjQTZDRUUzIiwgIiMxRjc4QjQiLCAiI0IyREY4QSIsICIjMzNBMDJDIiwgIiNGQjlBOTkiLCAiI0UzMUExQyIsICIjRkRCRjZGIiwgIiNDQUIyRDYiLCAiIzZBM0Q5QSIsICIjRkZGRjk5IiwgIiNCMTU5MjgiKQ0KDQojIFVzZSBjb3VudCBhbmQgY291bnRfbWF4IHRvIHNldCB0aGUgcmFuZ2Ugb2YgY29sb3IgbmVlZGVkIGZvciBlYWNoIGNvbHVtbi4NCiMgV2l0aCBzZXROYW1lcygpIHRoZSBjb2xvciBjb2RlIGlzIGxpbmtlZCB0byBlYWNoIHVuaXF1ZSBpbmRpdmlkdWFsIHZhbHVlIG9mIGVhY2ggY29sdW1uLg0KY291bnQ9MQ0KY29sb3JfbGlzdCA9IGxpc3QoKQ0KZm9yIChhbm4gaW4gYW5uX25hbWVzKSB7DQogIGNvdW50X21heCA9IGNvdW50K2xlbmd0aCh1bmlxdWUoRGF0YUBwaGVub0RhdGFAZGF0YVtbYW5uXV0pKS0xDQogIGNvbG9yX2xpc3RbW2Fubl1dPC1zZXROYW1lcyhjb2xvcltjb3VudDpjb3VudF9tYXhdLCB1bmlxdWUoRGF0YUBwaGVub0RhdGFAZGF0YVtbYW5uXV0pKQ0KICBjb3VudD1jb3VudF9tYXgrMQ0KfQ0KYGBgDQoNCmBgYHtyIGNvbG9yIHRhYmxlfQ0KdmFsdWVfbmFtZXMgPSBjKCkNCmZvciAoYW5uIGluIGFubl9uYW1lcykgew0KICB2YWx1ZV9uYW1lcyA8LSBjKHZhbHVlX25hbWVzLCB1bmlxdWUoRGF0YUBwaGVub0RhdGFAZGF0YVtbYW5uXV0pKQ0KfQ0KDQpjb2xvcl9kZiA8LSBhcy5kYXRhLmZyYW1lKHZhbHVlX25hbWVzKQ0KY29sb3JfZGYkY29sb3IgPC0gY29sb3JbMDpsZW5ndGgodmFsdWVfbmFtZXMpXQ0KDQpjb2xvcl9kZiAlPiUgDQogIG11dGF0ZShjb2xvciA9IGZjdF9pbm9yZGVyKGNvbG9yKSkgfD4gDQogIGd0KCkgJT4lIA0KICBkYXRhX2NvbG9yKGNvbHVtbnMgPSBjb2xvciwgY29sb3JzID0gYXMuY2hhcmFjdGVyKGNvbG9yKSkgJT4lDQogIHRhYl9vcHRpb25zKGNvbnRhaW5lci5oZWlnaHQgPSA1MDApDQpgYGANCg0KYGBge3J9DQpwYXN0ZSgiUmVhZHMgZnJvbSBmb2xsb3dpbmcgcnVucyB1c2VkOiAiLHVuaXF1ZShwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpJFNlcVNldElkKSkNCmBgYA0KDQojIDMgU3R1ZHkgZGVzaWduDQoNCmBgYHtyIGFubm90YXRlfQ0KcGtjcyA8LSBhbm5vdGF0aW9uKERhdGEpDQptb2R1bGVzIDwtIGdzdWIoIi5wa2MiLCAiIiwgcGtjcykNCmthYmxlKGRhdGEuZnJhbWUoUEtDcyA9IHBrY3MsIG1vZHVsZXMgPSBtb2R1bGVzKSkNCmBgYA0KDQpTZWxlY3QgdGhlIGFubm90YXRpb25zIHdlIHdhbnQgdG8gc2hvdywgdXNlIFxgXGAgdG8gc3Vycm91bmQgY29sdW1uDQpuYW1lcyB3aXRoIHNwYWNlcyBvciBzcGVjaWFsIHN5bWJvbHMNCg0KYGBge3Igc2VsZWN0X2Fubm90YXRpb25zfQ0KI2NvdW50X21hdCA8LSBkcGx5cjo6Y291bnQocERhdGEoRGF0YSksIEFOTjEsQU5OMixzbGlkZV9uYW1lKQ0KY291bnRfbWF0IDwtIHBEYXRhKERhdGEpICU+JSBkcGx5cjo6Y291bnQocERhdGEoRGF0YSlbYyhhbm5fbmFtZXMpXSkNCmBgYA0KDQpTaW1wbGlmeSB0aGUgc2xpZGUgbmFtZXMgaWYgcmVxdWlyZWQNCg0KYGBge3Igc2ltcGxpZnlfbmFtZXN9DQojIGNvdW50X21hdCRzbGlkZV9uYW1lIDwtIGdzdWIoImRpc2Vhc2UiLCAiZCIsIGdzdWIoIm5vcm1hbCIsICJuIiwgY291bnRfbWF0JHNsaWRlX25hbWUpKQ0KI2NvdW50X21hdCRwYXRoX2FubiA8LSBnc3ViKCJpIiwgIiIsIGNvdW50X21hdCRwYXRoX2FubikgI2NvcnJlY3Rpbmcgc3BlbGxpbmcgZXJyb3INCmBgYA0KDQpHYXRoZXIgdGhlIGRhdGEgYW5kIHBsb3QgaW4gb3JkZXI6IGNsYXNzLCBzbGlkZSBuYW1lLCByZWdpb24sIHNlZ21lbnQNCg0KYGBge3IgZ2F0aGVyX2RhdGF9DQp0ZXN0X2dyIDwtIGdhdGhlcl9zZXRfZGF0YShjb3VudF9tYXQsIDE6YWxsX29mKGFubl9zaXplKSkNCnRlc3RfZ3IkeCA8LSBmYWN0b3IodGVzdF9nciR4LCBsYWJlbHMgPSBhbm5fbmFtZXMpDQpgYGANCg0KUGxvdCBTYW5rZXkNCg0KYGBge3IgU2Fua2V5X3Bsb3QsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTExfQ0KZ2dwbG90KHRlc3RfZ3IsIGhlaWdodCA9IDEwLCB3aWR0aCA9IDEwLCBhZXMoeCwgaWQgPSBpZCwgc3BsaXQgPSB5LCB2YWx1ZSA9IG4pKSArDQogIGdlb21fcGFyYWxsZWxfc2V0cyhhZXMoZmlsbCA9IEFOTjIpLCBhbHBoYSA9IDAuNSwgYXhpcy53aWR0aCA9IDAuMSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfYXhlcyhheGlzLndpZHRoID0gMC4yKSArDQogIGdlb21fcGFyYWxsZWxfc2V0c19sYWJlbHMoY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSkgKw0KICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEyKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oMCkpICsgDQogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArDQogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInNlZ21lbnQiLCB4ID0gNC4yNSwgeGVuZCA9IDQuMjUsDQogICAgICAgICAgIHkgPSAxMCwgeWVuZCA9IDYxLCBsd2QgPSAyKSArDQogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSA0LjE5LCB5ID0gMjUsIGFuZ2xlID0gOTAsIHNpemUgPSA1LA0KICAgICAgICAgICBoanVzdCA9IDAuNSwgbGFiZWwgPSAiNTAgc2VnbWVudHMiKQ0KYGBgDQoNCiMgNCBRQyAmIFByZS1wcm9jZXNzaW5nDQoNClNoaWZ0IGNvdW50cyB0byBvbmUNCg0KYGBge3Igc2hpZnRfY291bnRzfQ0KI3NoaWZ0IGFueSBleHByZXNzaW9uIGNvdW50cyB3aXRoIGEgdmFsdWUgb2YgMCB0byAxIHRvIGVuYWJsZSBpbiBkb3duc3RyZWFtIHRyYW5zZm9ybWF0aW9ucy4NCkRhdGEgPC0gc2hpZnRDb3VudHNPbmUoRGF0YSwgdXNlREFMb2dpYyA9IFRSVUUpDQpgYGANCg0KIyA0LjEgU2VnbWVudCBRQw0KDQpXZSBmaXJzdCBhc3Nlc3Mgc2VxdWVuY2luZyBxdWFsaXR5IGFuZCBhZGVxdWF0ZSB0aXNzdWUgc2FtcGxpbmcgZm9yDQpldmVyeSBST0kvQU9JIHNlZ21lbnQuDQoNCkV2ZXJ5IFJPSS9BT0kgc2VnbWVudCB3aWxsIGJlIHRlc3RlZCBmb3I6DQoNClJhdyBzZXF1ZW5jaW5nIHJlYWRzOiBzZWdtZW50cyB3aXRoIFw+MTAwMCByYXcgcmVhZHMgYXJlIHJlbW92ZWQuICUNCkFsaWduZWQsJSBUcmltbWVkLCBvciAlIFN0aXRjaGVkIHNlcXVlbmNpbmcgcmVhZHM6IHNlZ21lbnRzIGJlbG93IFx+ODAlDQpmb3Igb25lIG9yIG1vcmUgb2YgdGhlc2UgUUMgcGFyYW1ldGVycyBhcmUgcmVtb3ZlZC4gJSBTZXF1ZW5jaW5nDQpzYXR1cmF0aW9uIChbMS1kZWR1cGxpY2F0ZWQgcmVhZHMvYWxpZ25lZCByZWFkc10lKTogc2VnbWVudHMgYmVsb3cgXH41MCUNCnJlcXVpcmUgYWRkaXRpb25hbCBzZXF1ZW5jaW5nIHRvIGNhcHR1cmUgZnVsbCBzYW1wbGUgZGl2ZXJzaXR5IGFuZCBhcmUNCm5vdCB0eXBpY2FsbHkgYW5hbHl6ZWQgdW50aWwgaW1wcm92ZWQuIE5lZ2F0aXZlIENvdW50OiB0aGlzIGlzIHRoZQ0KZ2VvbWV0cmljIG1lYW4gb2YgdGhlIHNldmVyYWwgdW5pcXVlIG5lZ2F0aXZlIHByb2JlcyBpbiB0aGUgR2VvTXggcGFuZWwNCnRoYXQgZG8gbm90IHRhcmdldCBtUk5BIGFuZCBlc3RhYmxpc2ggdGhlIGJhY2tncm91bmQgY291bnQgbGV2ZWwgcGVyDQpzZWdtZW50OyBzZWdtZW50cyB3aXRoIGxvdyBuZWdhdGl2ZSBjb3VudHMgKDEtMTApIGFyZSBub3QgbmVjZXNzYXJpbHkNCnJlbW92ZWQgYnV0IG1heSBiZSBzdHVkaWVkIGNsb3NlciBmb3IgbG93IGVuZG9nZW5vdXMgZ2VuZSBzaWduYWwgYW5kL29yDQppbnN1ZmZpY2llbnQgdGlzc3VlIHNhbXBsaW5nLiBObyBUZW1wbGF0ZSBDb250cm9sIChOVEMpIGNvdW50OiB2YWx1ZXMNClw+MSwwMDAgY291bGQgaW5kaWNhdGUgY29udGFtaW5hdGlvbiBmb3IgdGhlIHNlZ21lbnRzIGFzc29jaWF0ZWQgd2l0aA0KdGhpcyBOVEM7IGhvd2V2ZXIsIGluIGNhc2VzIHdoZXJlIHRoZSBOVEMgY291bnQgaXMgYmV0d2VlbiAxLDAwMC0NCjEwLDAwMCwgdGhlIHNlZ21lbnRzIG1heSBiZSB1c2VkIGlmIHRoZSBOVEMgZGF0YSBpcyB1bmlmb3JtbHkgbG93IChlLmcuDQowLTIgY291bnRzIGZvciBhbGwgcHJvYmVzKS4gTnVjbGVpOiBcPjEwMCBudWNsZWkgcGVyIHNlZ21lbnQgaXMNCmdlbmVyYWxseSByZWNvbW1lbmRlZDsgaG93ZXZlciwgdGhpcyBjdXRvZmYgaXMgaGlnaGx5IHN0dWR5L3Rpc3N1ZQ0KZGVwZW5kZW50IGFuZCBtYXkgbmVlZCB0byBiZSByZWR1Y2VkOyB3aGF0IGlzIG1vc3QgaW1wb3J0YW50IGlzDQpjb25zaXN0ZW5jeSBpbiB0aGUgbnVjbGVpIGRpc3RyaWJ1dGlvbiBmb3Igc2VnbWVudHMgd2l0aGluIHRoZSBzdHVkeS4NCkFyZWE6IGdlbmVyYWxseSBjb3JyZWxhdGVzIHdpdGggbnVjbGVpOyBhIHN0cmljdCBjdXRvZmYgaXMgbm90IGdlbmVyYWxseQ0KYXBwbGllZCBiYXNlZCBvbiBhcmVhLg0KDQojIDQuMS4xIFNlbGVjdCBTZWdtZW50IFFDDQoNCkZpcnN0LCB3ZSBzZWxlY3QgdGhlIFFDIHBhcmFtZXRlciBjdXRvZmZzLCBhZ2FpbnN0IHdoaWNoIG91ciBST0kvQU9JDQpzZWdtZW50cyB3aWxsIGJlIHRlc3RlZCBhbmQgZmxhZ2dlZCBhcHByb3ByaWF0ZWx5LiBXZSBoYXZlIHNlbGVjdGVkIHRoZQ0KYXBwcm9wcmlhdGUgc3R1ZHktc3BlY2lmaWMgcGFyYW1ldGVycyBmb3IgdGhpcyBzdHVkeS4gTm90ZTogdGhlIGRlZmF1bHQNClFDIHZhbHVlcyByZWNvbW1lbmRlZCBhYm92ZSBhcmUgYWR2aXNlZCB3aGVuIHN1cnZleWluZyBhIG5ldyBkYXRhc2V0IGZvcg0KdGhlIGZpcnN0IHRpbWUuDQoNCkRlZmF1bHQgUUMgY3V0b2ZmcyBhcmUgY29tbWVudGVkIGluICgpIGFkamFjZW50IHRvIHRoZSByZXNwZWN0aXZlDQpwYXJhbWV0ZXJzIHN0dWR5LXNwZWNpZmljIHZhbHVlcyB3ZXJlIHNlbGVjdGVkIGFmdGVyIHZpc3VhbGl6aW5nIHRoZSBRQw0KcmVzdWx0cyBpbiBtb3JlIGRldGFpbCBiZWxvdw0KDQpgYGB7ciBzZXRfUUNfcGFyYW1zfQ0KUUNfcGFyYW1zIDwtDQogIGxpc3QobWluU2VnbWVudFJlYWRzID0gMTAwMCwgIyBNaW5pbXVtIG51bWJlciBvZiByZWFkcyAoMTAwMCkNCiAgICAgICBwZXJjZW50VHJpbW1lZCA9IDgwLCAgICAjIE1pbmltdW0gJSBvZiByZWFkcyB0cmltbWVkICg4MCUpDQogICAgICAgcGVyY2VudFN0aXRjaGVkID0gODAsICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgc3RpdGNoZWQgKDgwJSkNCiAgICAgICBwZXJjZW50QWxpZ25lZCA9IDc1LCAgICAjIE1pbmltdW0gJSBvZiByZWFkcyBhbGlnbmVkICg4MCUpDQogICAgICAgcGVyY2VudFNhdHVyYXRpb24gPSA1MCwgIyBNaW5pbXVtIHNlcXVlbmNpbmcgc2F0dXJhdGlvbiAoNTAlKQ0KICAgICAgIG1pbk5lZ2F0aXZlQ291bnQgPSAxLCAgICMgTWluaW11bSBuZWdhdGl2ZSBjb250cm9sIGNvdW50cyAoMTApDQogICAgICAgbWF4TlRDQ291bnQgPSA5MDAwLCAgICAgIyBNYXhpbXVtIGNvdW50cyBvYnNlcnZlZCBpbiBOVEMgd2VsbCAoMTAwMCkNCiAgICAgICBtaW5OdWNsZWkgPSAyMCwgICAgICAgICMgTWluaW11bSAjIG9mIG51Y2xlaSBlc3RpbWF0ZWQgKDEwMCkNCiAgICAgICBtaW5BcmVhID0gMTAwMCkgICAgICAgICAjIE1pbmltdW0gc2VnbWVudCBhcmVhICg1MDAwKQ0KRGF0YSA8LQ0KICBzZXRTZWdtZW50UUNGbGFncyhEYXRhLCBxY0N1dG9mZnMgPSBRQ19wYXJhbXMpICAgICAgICANCg0KY2F0KCJwcmUtUUMgZmVhdHVyZXM6IiwgZGltKERhdGEpWzFdLCAiXG5wcmUtUUMgc2FtcGxlczoiLCBkaW0oRGF0YSlbMl0pDQoNCiNUYWJsZSBmb3IgY2xhcmlmaWNhdGlvbg0KUUNwYXJhbXNfZGYgPC0gZGF0YS5mcmFtZSAoDQogIGl0ZW1zID0gYygibWluU2VnbWVudFJlYWRzIiwicGVyY2VudFRyaW1tZWQiLCJwZXJjZW50U3RpdGNoZWQiLCJwZXJjZW50QWxpZ25lZCIsInBlcmNlbnRTYXR1cmF0aW9uIiwNCiAgICAgICAgICAgICJtaW5OZWdhdGl2ZUNvdW50IiwibWF4TlRDQ291bnQiLCJtaW5OdWNsZWkiLCJtaW5BcmVhIiksDQogIGRlZmF1bHRzID0gYygxMDAwLDgwLDgwLDgwLDUwLDEwLDEwMDAsMTAwLDUwMDApLA0KICBhY3R1YWwgPSBjKFFDX3BhcmFtcyRtaW5TZWdtZW50UmVhZHMsUUNfcGFyYW1zJHBlcmNlbnRUcmltbWVkLFFDX3BhcmFtcyRwZXJjZW50U3RpdGNoZWQsUUNfcGFyYW1zJHBlcmNlbnRBbGlnbmVkLFFDX3BhcmFtcyRwZXJjZW50U2F0dXJhdGlvbixRQ19wYXJhbXMkbWluTmVnYXRpdmVDb3VudCxRQ19wYXJhbXMkbWF4TlRDQ291bnQsUUNfcGFyYW1zJG1pbk51Y2xlaSxRQ19wYXJhbXMkbWluQXJlYSkNCikNCg0KZGF0YXRhYmxlKFFDcGFyYW1zX2RmLCByb3duYW1lcz1GQUxTRSwNCiAgICAgICAgICBjYXB0aW9uID0gIlFDIHRocmVzaG9sZHMiLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKQ0KKQ0KDQpgYGANCg0KQ29sbGF0ZSBRQyBSZXN1bHRzDQoNCmBgYHtyIGNvbGxhdGVfUUNfcmVzdWx0c30NClFDUmVzdWx0cyA8LSBwcm90b2NvbERhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dDQpmbGFnX2NvbHVtbnMgPC0gY29sbmFtZXMoUUNSZXN1bHRzKQ0KUUNfU3VtbWFyeSA8LSBkYXRhLmZyYW1lKFBhc3MgPSBjb2xTdW1zKCFRQ1Jlc3VsdHNbLCBmbGFnX2NvbHVtbnNdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBXYXJuaW5nID0gY29sU3VtcyhRQ1Jlc3VsdHNbLCBmbGFnX2NvbHVtbnNdKSkNCg0KUUNSZXN1bHRzJFFDU3RhdHVzIDwtIGFwcGx5KFFDUmVzdWx0cywgMUwsIGZ1bmN0aW9uKHgpIHsNCiAgaWZlbHNlKHN1bSh4KSA9PSAwTCwgIlBBU1MiLCAiV0FSTklORyIpDQp9KQ0KDQpRQ19TdW1tYXJ5WyJUT1RBTCBGTEFHUyIsIF0gPC0NCiAgYyhzdW0oUUNSZXN1bHRzWywgIlFDU3RhdHVzIl0gPT0gIlBBU1MiKSwNCiAgICBzdW0oUUNSZXN1bHRzWywgIlFDU3RhdHVzIl0gPT0gIldBUk5JTkciKSkNCg0KY29sX2J5IDwtICJBTk4xIg0KY29sX2J5X3BsYXRlIDwtICJQbGF0ZV9JRCINCmBgYA0KDQojIDQuMiBHcmFwaGljYWwgc3VtbWFyaWVzIG9mIFFDIHN0YXRpc3RpY3Mgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KYGBge3IgUUNfcGxvdHRpbmd9DQpRQ19oaXN0b2dyYW0gPC0gZnVuY3Rpb24oYXNzYXlfZGF0YSA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbiA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbF9ieSA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgdGhyID0gTlVMTCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV90cmFucyA9IE5VTEwpIHsNCiAgcGx0IDwtIGdncGxvdChhc3NheV9kYXRhLA0KICAgICAgICAgICAgICAgIGFlc19zdHJpbmcoeCA9IHBhc3RlMCgidW5saXN0KGAiLCBhbm5vdGF0aW9uLCAiYCkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmaWxsX2J5KSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHRociwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKw0KICAgIHRoZW1lX2J3KCkgKyBndWlkZXMoZmlsbCA9ICJub25lIikgKw0KICAgIGZhY2V0X3dyYXAoYXMuZm9ybXVsYShwYXN0ZSgifiIsIGZpbGxfYnkpKSwgbnJvdyA9IDQpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JfbGlzdCRBTk4xKSArDQogICAgbGFicyh4ID0gYW5ub3RhdGlvbiwgeSA9ICJzZWdtZW50cywgIyIsIHRpdGxlID0gYW5ub3RhdGlvbikNCiAgaWYoIWlzLm51bGwoc2NhbGVfdHJhbnMpKSB7DQogICAgcGx0IDwtIHBsdCArDQogICAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSBzY2FsZV90cmFucykNCiAgfQ0KICBwbHQNCn0NCmBgYA0KDQojIyBUcmltbWVkDQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAiVHJpbW1lZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50VHJpbW1lZCkNCmBgYA0KDQojIyBTdGljaGVkICglKQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIlN0aXRjaGVkICglKSIsIGNvbF9ieSwgUUNfcGFyYW1zJHBlcmNlbnRTdGl0Y2hlZCkNCmBgYA0KDQojIyBBbGlnbmVkICglKQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIkFsaWduZWQgKCUpIiwgY29sX2J5LFFDX3BhcmFtcyRwZXJjZW50QWxpZ25lZCkNCmBgYA0KDQojIyBTZXF1ZW5jaW5nIFNhdHVyYXRpb24gKCUpIHsuYWN0aXZlfQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIlNhdHVyYXRlZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50U2F0dXJhdGlvbikgKw0KICBsYWJzKHRpdGxlID0gIlNlcXVlbmNpbmcgU2F0dXJhdGlvbiAoJSkiLA0KICAgICAgIHggPSAiU2VxdWVuY2luZyBTYXR1cmF0aW9uICglKSIpDQpgYGANCg0KIyMgQXJlYQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgImFyZWEiLCBjb2xfYnksIFFDX3BhcmFtcyRtaW5BcmVhLCBzY2FsZV90cmFucyA9ICJsb2cxMCIpDQpgYGANCg0KIyMgTnVjbGVpIGNvdW50DQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAibnVjbGVpIiwgY29sX2J5LCBRQ19wYXJhbXMkbWluTnVjbGVpKQ0KYGBgDQoNCiMjIER1cGxpY2F0aW9uUmF0ZQ0KDQpgYGB7cn0NCmdncGxvdChwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpLA0KICAgICAgIGFlcyh4ID0gUGxhdGVfSUQsIGZpbGw9UGxhdGVfSUQsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9SYXcpKSkgKw0KICBnZW9tX3Zpb2xpbigpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAuMikgKw0KICBsYWJzKHkgPSAiRGVkdXBsaWNhdGVkIC8gUmF3IHJlYWRzIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyBOZWdwcm9iZXMgdnMgRW5kb2dlbm91cw0KDQpgYGB7ciBwbG90X25lZ3Byb2JlX2RhdGEsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTV9DQp0bXBfdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQoNCiNnZXQgbmVnYXRpdmUgcHJvYmUgZGF0YQ0KbmVnczwtc3Vic2V0KHRtcF90YXJnZXRfRGF0YSxDb2RlQ2xhc3M9PSJOZWdhdGl2ZSIpDQoNCnAxPC1nZ3Bsb3QocERhdGEobmVncyksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICB5ID0gYXNzYXlEYXRhRWxlbWVudChuZWdzLCBlbHQgPSAiZXhwcnMiKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIk5lZ2F0aXZlIHByb2JlcyBFeHByZXNzaW9uIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwMDApLCB0cmFucyA9ICJsb2cyIikgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQoNCg0KIyBnZXQgZW5kb2dlbm91cyBwcm9iZSBkYXRhDQplbmQ8LXN1YnNldCh0bXBfdGFyZ2V0X0RhdGEsQ29kZUNsYXNzPT0iRW5kb2dlbm91cyIpDQoNCnAyPC1nZ3Bsb3QocERhdGEoZW5kKSwNCiAgICAgICBhZXMoeCA9IEFOTjIsIGZpbGwgPSBBTk4yLA0KICAgICAgICAgICB5ID0gY29sTWVhbnMoYXNzYXlEYXRhRWxlbWVudChlbmQsIGVsdCA9ICJleHBycyIpKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkVuZG9nZW5vdXMgcHJvYmVzIEV4cHJlc3Npb24gKG1lYW4pIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwMDApLHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCg0KcGwgPC1saXN0KHAxLHAyKQ0KcGxvdF9ncmlkKHBsb3RsaXN0PXBsLCBucm93PTIsIGFsaWduPSd2JykNCg0KYGBgDQoNCiMjIE5lZ19wcm9iZSByZWFkcyBjb21wYXJlZCB0byByYXdfcmVhZHMNCg0KYGBge3J9DQoNCiMgbWFrZSBiYWNrZ3JvdW5kIHRvdGFsIG5lZyBwcm9iZSBjb3VudA0KZmRhdGFfZGY8LWZEYXRhKERhdGEpDQpuZWdwcm9iZXNuYW1lczwtcm93bmFtZXMoZmRhdGFfZGZbZmRhdGFfZGYkTmVnYXRpdmU9PVRSVUUsXSkNCnRlbXBfZXhwPC1hc3NheURhdGFFbGVtZW50KERhdGEsZWx0PSdleHBycycpDQpuZWdwcm9iZV9leHByX2ZkPC10ZW1wX2V4cFtyb3duYW1lcyh0ZW1wX2V4cCkgJWluJSBuZWdwcm9iZXNuYW1lcyxdDQp0b3RfbmVnX2N0cmxfcmVhZHM8LWNvbFN1bXMobmVncHJvYmVfZXhwcl9mZCkNCnRvdF9kZWR1cF9yZWFkczwtcERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSREZWR1cGxpY2F0ZWRSZWFkcw0KDQpkZjwtZGF0YS5mcmFtZSgnYW9pJz0gbmFtZXModG90X25lZ19jdHJsX3JlYWRzKSwndG90X2RlZHVwX3JlYWRzJyA9IGFzLm51bWVyaWModG90X2RlZHVwX3JlYWRzKSwndG90X25lZ19jdHJsX3JlYWRzJz1hcy5udW1lcmljKHRvdF9uZWdfY3RybF9yZWFkcykpDQpkZjwtbWVsdChkZixpZD0iYW9pIikNCmdncGxvdChkZixhZXMoZmlsbD12YXJpYWJsZSx5PXZhbHVlLHg9YW9pKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb249ImlkZW50aXR5IixzdGF0PSJpZGVudGl0eSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gbG9nMl90cmFucygpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIixheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSxheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSAgICAgICAgICAgICAgICAgICAgIA0KIA0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgQmFja2dyb3VuZA0KDQpgYGB7cn0NCiMgZ2V0IGRjYyBwZXIgcGxhdGUuIHN1bSBuZWdwcm9iZSBjb3VudHMvZGNjL3BsYXRlDQpnZ3Bsb3QocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSwNCiAgICAgICBhZXMoeCA9IFBsYXRlX0lELCBmaWxsPVBsYXRlX0lELA0KICAgICAgICAgIHkgPSBEZWR1cGxpY2F0ZWRSZWFkcykpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCAvIFJhdyByZWFkcyIpICsNCiAgc2NhbGVfeV9sb2cxMCgpKw0KICBnZW9tX2hsaW5lKGRhdGEgPXBEYXRhKHByb3RvY29sRGF0YShEYXRhKSkgLCANCiAgICAgICAgICAgYWVzKHlpbnRlcmNlcHQgPSBOVEMsIGNvbG91cj1QbGF0ZV9JRCkpICsNCiAgdGhlbWVfYncoKQ0KDQpgYGANCg0KIyMgRHVwbGljYXRlZCByZWFkcyB2cyBST0lhcmVhDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD01fQ0KdGVtcF9kZjwtY2JpbmQocERhdGEoRGF0YSkscERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSxkY2M9cm93bmFtZXMocERhdGEoRGF0YSkpKQ0KDQpnZ3Bsb3QodGVtcF9kZiwNCiAgICAgICBhZXMoeCA9IGRjYywgY29sb3VyPXNsaWRlX25hbWUsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9hcmVhKSApKSArDQogIGdlb21fcG9pbnQoKSArDQogIHlsaW0oMCwyMCkgKyANCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCByZWFkcyAvIFJPSSBhcmVhIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID02LCBhbmdsZT05MCwgaGp1c3Q9MSkgKQ0KDQpgYGANCg0KIyMgRHVwbGljYXRlZCByZWFkcyB2cyBudWNsZWkNCg0KYGBge3IsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTV9DQp0ZW1wX2RmPC1jYmluZChwRGF0YShEYXRhKSxwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpLGRjYz1yb3duYW1lcyhwRGF0YShEYXRhKSkpDQoNCmdncGxvdCh0ZW1wX2RmLA0KICAgICAgIGFlcyh4ID0gZGNjLCBjb2xvdXI9c2xpZGVfbmFtZSwNCiAgICAgICAgICB5ID0gKERlZHVwbGljYXRlZFJlYWRzL251Y2xlaSkgKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB5bGltKDAsNTApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEFkanVzdCBwZXIgcHJvamVjdA0KICBsYWJzKHkgPSAiRGVkdXBsaWNhdGVkIHJlYWRzIC8gbnVjbGVpIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID02LCBhbmdsZT05MCwgaGp1c3Q9MSkgKQ0KDQpgYGANCg0KIyA0LjMgUHJvY2VzcyBOZWdhdGl2ZSBHZW9NZWFucw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBuZWdhdGl2ZSBnZW9tZXRyaWMgbWVhbnMgZm9yIGVhY2ggbW9kdWxlDQojIEl0IHdpbGwgc2hvdyBvbmx5IHRoZSBuZWdhdGl2ZSBwcm9iZXMgZ2VvbWVhbiwgc28gZXhwZWN0IGxlc3Mgc2VnbWVudHMuDQpuZWdhdGl2ZUdlb01lYW5zIDwtIA0KICBlc0J5KG5lZ2F0aXZlQ29udHJvbFN1YnNldChEYXRhKSwgDQogICAgICAgR1JPVVAgPSAiTW9kdWxlIiwgDQogICAgICAgRlVOID0gZnVuY3Rpb24oeCkgeyANCiAgICAgICAgIGFzc2F5RGF0YUFwcGx5KHgsIE1BUkdJTiA9IDIsIEZVTiA9IG5nZW9NZWFuLCBlbHQgPSAiZXhwcnMiKSANCiAgICAgICB9KSANCnByb3RvY29sRGF0YShEYXRhKVtbIk5lZ0dlb01lYW4iXV0gPC0gbmVnYXRpdmVHZW9NZWFucw0KDQpuZWdDb2xzIDwtIHBhc3RlMCgiTmVnR2VvTWVhbl8iLCBtb2R1bGVzKQ0KcERhdGEoRGF0YSlbLCBuZWdDb2xzXSA8LSBzRGF0YShEYXRhKVtbIk5lZ0dlb01lYW4iXV0NCmZvcihhbm4gaW4gbmVnQ29scykgew0KICBwbHQgPC0gUUNfaGlzdG9ncmFtKHBEYXRhKERhdGEpLCBhbm4sIGNvbF9ieSwgMiwgc2NhbGVfdHJhbnMgPSAibG9nMTAiKQ0KICBwcmludChwbHQpDQp9DQoNCg0KIyBEZXRhdGNoIG5lZ19nZW9tZWFuIGNvbHVtbnMgYWhlYWQgb2YgYWdncmVnYXRlQ291bnRzIGNhbGwNCg0KcERhdGEoRGF0YSkgPC0gcERhdGEoRGF0YSlbLCAhY29sbmFtZXMocERhdGEoRGF0YSkpICVpbiUgbmVnQ29sc10NCg0KYGBgDQoNClNob3cgYWxsIE5UQyB2YWx1ZXMsIEZyZXEgPSBcIyBvZiBTZWdtZW50cyB3aXRoIGEgZ2l2ZW4gTlRDIGNvdW50Og0KDQpgYGB7ciBRQ190YWJsZXN9DQpRQzwtc0RhdGEoRGF0YSkNCg0KbnRjX2RmIDwtUUNbLGMoInNsaWRlX25hbWUiLCJQbGF0ZV9JRCIsIk5UQ19JRCIsIk5UQyIpXQ0KdGVtcHRhYmxlPC1udGNfZGYgJT4lIGRwbHlyOjpjb3VudChudGNfZGYkc2xpZGVfbmFtZSxudGNfZGYkTlRDX0lELG50Y19kZiRQbGF0ZV9JRCxudGNfZGYkTlRDKQ0KY29sbmFtZXModGVtcHRhYmxlKSA8LSBjKCJTbGlkZV9uYW1lIiwiTlRDX0lEIiwiUGxhdGVfSUQiLCJOVENfY291bnQiLCJOdW1iZXJfb2Zfc2FtcGxlcyIpDQpkYXRhdGFibGUodGVtcHRhYmxlLCByb3duYW1lcyA9IEZBTFNFKQ0KDQoNCmthYmxlKHRhYmxlKE5UQ19Db3VudCA9IHNEYXRhKERhdGEpJE5UQyksIGNvbC5uYW1lcyA9IGMoIk5UQyBDb3VudCIsICIjIG9mIFNlZ21lbnRzIikpDQoNCmthYmxlKFFDX1N1bW1hcnksIGNhcHRpb24gPSAiUUMgU3VtbWFyeSBUYWJsZSBmb3IgZWFjaCBTZWdtZW50IikNCg0KZGF0YXRhYmxlKFFDX1N1bW1hcnksDQogICAgICAgICAgY2FwdGlvbiA9ICJBT0kgUUMgU3VtbWFyeSIsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApDQopDQpgYGANCg0KU2hvdyBBT0lzIHdoaWNoIGZhaWwgY3JpdGljYWwgUUNzLg0KDQpgYGB7ciBsaXN0X2ZhaWx1cmVzfQ0KUUM8LXNEYXRhKERhdGEpDQp1bmRlcnNhdDwtc3Vic2V0KFFDLCBgU2F0dXJhdGVkICglKWA8PSBRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24pDQoNCmlmKG5yb3codW5kZXJzYXQpPiAwKSB7DQoNCmRhdGF0YWJsZShhZ2dyZWdhdGUodW5kZXJzYXQsIGJ5PWxpc3QodW5kZXJzYXQkU2FtcGxlSUQpLHBhc3RlLGNvbGxhcHNlPSI7IiksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApDQopfQ0KYGBgDQoNClN1YnNldHRpbmcgb3VyIGRhdGFzZXQgaGFzIHJlbW92ZWQgc2FtcGxlcyB3aGljaCBkaWQgbm90IHBhc3MgUUMNCg0KYGBge3Igc3Vic2V0dGluZ19RQ19mYWlsc30NCkRhdGEgPC0gRGF0YVssIFFDUmVzdWx0cyRRQ1N0YXR1cyA9PSAiUEFTUyJdDQpgYGANCg0KR2VuZXJhbGx5IGtlZXAgdGhlIHFjQ3V0b2ZmcyBwYXJhbWV0ZXJzIHVuY2hhbmdlZC4gU2V0DQpyZW1vdmVMb2NhbE91dGxpZXJzIHRvIEZBTFNFIGlmIHlvdSBkbyBub3Qgd2FudCB0byByZW1vdmUgbG9jYWwgb3V0bGllcnMNCg0KYGBge3IgcHJvY2Vzc19RQ30NCkRhdGEgPC0gc2V0QmlvUHJvYmVRQ0ZsYWdzKERhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHFjQ3V0b2ZmcyA9IGxpc3QobWluUHJvYmVSYXRpbyA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnRGYWlsR3J1YmJzID0gMjApLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW1vdmVMb2NhbE91dGxpZXJzID0gRkFMU0UpDQoNClByb2JlUUNSZXN1bHRzIDwtIGZEYXRhKERhdGEpW1siUUNGbGFncyJdXQ0KYGBgDQoNCkRlZmluZSBRQyB0YWJsZSBmb3IgUHJvYmUgUUMNCg0KYGBge3IgZGVmaW5lX3FjX3RhYmxlfQ0KcWNfZGYgPC0gZGF0YS5mcmFtZShQYXNzZWQgPSBzdW0ocm93U3VtcyhQcm9iZVFDUmVzdWx0c1ssIC0xXSkgPT0gMCksDQogICAgICAgICAgICAgICAgICAgIEdsb2JhbCA9IHN1bShQcm9iZVFDUmVzdWx0cyRHbG9iYWxHcnViYnNPdXRsaWVyKSwNCiAgICAgICAgICAgICAgICAgICAgTG9jYWwgPSBzdW0ocm93U3VtcyhQcm9iZVFDUmVzdWx0c1ssIC0yOi0xXSkgPiAwDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICYgIVByb2JlUUNSZXN1bHRzJEdsb2JhbEdydWJic091dGxpZXIpKQ0KYGBgDQoNClN1YnNldCBvYmplY3QgdG8gZXhjbHVkZSBhbGwgdGhhdCBkaWQgbm90IHBhc3MgUmF0aW8gJiBHbG9iYWwgdGVzdGluZw0KDQpgYGB7ciBzdWJzZXR9DQpQcm9iZVFDUGFzc2VkIDwtIA0KICBzdWJzZXQoRGF0YSwgDQogICAgICAgICBmRGF0YShEYXRhKVtbIlFDRmxhZ3MiXV1bLGMoIkxvd1Byb2JlUmF0aW8iKV0gPT0gRkFMU0UgJg0KICAgICAgICAgICBmRGF0YShEYXRhKVtbIlFDRmxhZ3MiXV1bLGMoIkdsb2JhbEdydWJic091dGxpZXIiKV0gPT0gRkFMU0UpDQoNCkRhdGEgPC0gUHJvYmVRQ1Bhc3NlZCANCmNhdCgiQWZ0ZXIgUUMgZmVhdHVyZXM6IiwgZGltKERhdGEpWzFdLCAiXG5BZnRlciBRQyBzYW1wbGVzOiIsIGRpbShEYXRhKVsyXSkNCmBgYA0KDQpDaGVjayBob3cgbWFueSB1bmlxdWUgdGFyZ2V0cyB0aGUgb2JqZWN0IGhhcw0KDQpgYGB7ciB1bmlxdWVfY2hlY2t9DQpsZW5ndGgodW5pcXVlKGZlYXR1cmVEYXRhKERhdGEpW1siVGFyZ2V0TmFtZSJdXSkpDQpgYGANCg0KQ29sbGFwc2UgdG8gdGFyZ2V0cw0KDQpgYGB7ciBjb2xsYXBzX3RhcmdldHN9DQp0YXJnZXRfRGF0YSA8LSBhZ2dyZWdhdGVDb3VudHMoRGF0YSkNCg0KZXhwcnModGFyZ2V0X0RhdGEpWzE6NSwgMToyXQ0KYGBgDQoNCkRlZmluZSBMT1EgU0QgdGhyZXNob2xkIGFuZCBtaW5pbXVtIHZhbHVlDQoNCmBgYHtyIHNldF9MU1F9DQpjdXRvZmYgPC0gMg0KbWluTE9RIDwtIDINCmBgYA0KDQojIDQuNCBMaW1pdCBvZiBRdWFudGlmaWNhdGlvbg0KDQpXZSBkZWZpbmUgYSBsaW1pdCBvZiBxdWFudGlmaWNhdGlvbiAoTE9RKSBwZXIgUk9JL0FPSSBzZWdtZW50IGJhc2VkIG9uDQp0aGUgbmVnYXRpdmUgY29udHJvbCBwcm9iZXMgdG8gZ3VpZGUgdGhlIGZpbHRlcmluZyBvZiBzZWdtZW50cyBhbmQgZ2VuZXMNCndpdGggbG93IHNpZ25hbCByZWxhdGl2ZSB0byBiYWNrZ3JvdW5kLiBUaGUgZm9ybXVsYSBmb3IgY2FsY3VsYXRpbmcgdGhlDQpMT1EgaW4gdGhlICRpXnt0aH0kIHNlZ21lbnQgYXQgJG4kIHN0YW5kYXJkIGRldmlhdGlvbnMgKCRuID0gMiQgZm9yIHRoaXMNCnN0dWR5KSBpczogJExPUV9pPWdlb21lYW4oTmVnUHJvYmVfaSkqZ2VvU0QoTmVnUHJvYmVfaSlebiQNCg0KQ2FsY3VsYXRlIExPUSBwZXIgbW9kdWxlIHRlc3RlZA0KDQpgYGB7ciBjYWxjdWxhdGVfTE9RfQ0KTE9RIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXModGFyZ2V0X0RhdGEpKQ0KZm9yKG1vZHVsZSBpbiBtb2R1bGVzKSB7DQogIHZhcnMgPC0gcGFzdGUwKGMoIk5lZ0dlb01lYW5fIiwgIk5lZ0dlb1NEXyIpLA0KICAgICAgICAgICAgICAgICBtb2R1bGUpDQogIGlmKGFsbCh2YXJzWzE6Ml0gJWluJSBjb2xuYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSkgew0KICAgIExPUVssIG1vZHVsZV0gPC0NCiAgICAgIHBtYXgobWluTE9RLA0KICAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCB2YXJzWzFdXSAqIA0KICAgICAgICAgICAgIHBEYXRhKHRhcmdldF9EYXRhKVssIHZhcnNbMl1dIF4gY3V0b2ZmKQ0KICB9DQp9DQpwRGF0YSh0YXJnZXRfRGF0YSkkTE9RIDwtIExPUQ0KYGBgDQoNCiMgNC41IEZpbHRlcmluZw0KDQpBZnRlciBkZXRlcm1pbmluZyB0aGUgbGltaXQgb2YgcXVhbnRpZmljYXRpb24gKExPUSkgcGVyIHNlZ21lbnQsIHdlDQpyZWNvbW1lbmQgZmlsdGVyaW5nIG91dCBlaXRoZXIgc2VnbWVudHMgYW5kL29yIGdlbmVzIHdpdGggYWJub3JtYWxseSBsb3cNCnNpZ25hbC4gRmlsdGVyaW5nIGlzIGFuIGltcG9ydGFudCBzdGVwIHRvIGZvY3VzIG9uIHRoZSB0cnVlIGJpb2xvZ2ljYWwNCmRhdGEgb2YgaW50ZXJlc3QuDQoNCldlIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkIGluIGVhY2ggc2VnbWVudCBhY3Jvc3MgdGhlDQpkYXRhc2V0Lg0KDQpgYGB7ciBmaWx0ZXJpbmd9DQpMT1FfTWF0IDwtIGMoKQ0KZm9yKG1vZHVsZSBpbiBtb2R1bGVzKSB7DQogIGluZCA8LSBmRGF0YSh0YXJnZXRfRGF0YSkkTW9kdWxlID09IG1vZHVsZQ0KICBNYXRfaSA8LSB0KGVzQXBwbHkodGFyZ2V0X0RhdGFbaW5kLCBdLCBNQVJHSU4gPSAxLA0KICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgICAgICAgICB4ID4gTE9RWywgbW9kdWxlXQ0KICAgICAgICAgICAgICAgICAgICAgfSkpDQogIExPUV9NYXQgPC0gcmJpbmQoTE9RX01hdCwgTWF0X2kpDQp9DQojIGVuc3VyZSBvcmRlcmluZyBzaW5jZSB0aGlzIGlzIHN0b3JlZCBvdXRzaWRlIG9mIHRoZSBnZW9teFNldA0KTE9RX01hdCA8LSBMT1FfTWF0W2ZEYXRhKHRhcmdldF9EYXRhKSRUYXJnZXROYW1lLCBdDQpgYGANCg0KIyA0LjUuMSBTZWdtZW50IEdlbmUgRGV0ZWN0aW9uDQoNCldlIGZpcnN0IGZpbHRlciBvdXQgc2VnbWVudHMgd2l0aCBleGNlcHRpb25hbGx5IGxvdyBzaWduYWwuIFRoZXNlDQpzZWdtZW50cyB3aWxsIGhhdmUgYSBzbWFsbCBmcmFjdGlvbiBvZiBwYW5lbCBnZW5lcyBkZXRlY3RlZCBhYm92ZSB0aGUNCkxPUSByZWxhdGl2ZSB0byB0aGUgb3RoZXIgc2VnbWVudHMgaW4gdGhlIHN0dWR5LiBMZXQncyB2aXN1YWxpemUgdGhlDQpkaXN0cmlidXRpb24gb2Ygc2VnbWVudHMgd2l0aCByZXNwZWN0IHRvIHRoZWlyICUgZ2VuZXMgZGV0ZWN0ZWQ6DQoNClNhdmUgZGV0ZWN0aW9uIHJhdGUgaW5mb3JtYXRpb24gdG8gcGhlbm8gZGF0YQ0KDQpgYGB7ciBzYXZlX2RldGVjdGlub19yYXRlfQ0KcERhdGEodGFyZ2V0X0RhdGEpJEdlbmVzRGV0ZWN0ZWQgPC0gDQogIGNvbFN1bXMoTE9RX01hdCwgbmEucm0gPSBUUlVFKQ0KcERhdGEodGFyZ2V0X0RhdGEpJEdlbmVEZXRlY3Rpb25SYXRlIDwtDQogIHBEYXRhKHRhcmdldF9EYXRhKSRHZW5lc0RldGVjdGVkIC8gbnJvdyh0YXJnZXRfRGF0YSkNCg0KYGBgDQoNCkRldGVybWluZSBkZXRlY3Rpb24gdGhyZXNob2xkczogMSUsIDUlLCAxMCUsIDE1JSwgXD4xNSUNCg0KYGBge3IgZGV0ZXJtaW5lK3RocmVzaG9sZHN9DQpwRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uVGhyZXNob2xkIDwtIA0KICBjdXQocERhdGEodGFyZ2V0X0RhdGEpJEdlbmVEZXRlY3Rpb25SYXRlLA0KICAgICAgYnJlYWtzID0gYygwLCAwLjAxLCAwLjA1LCAwLjEsIDAuMTUsIDAuMiwxKSwNCiAgICAgIGxhYmVscyA9IGMoIjwxJSIsICIxLTUlIiwgIjUtMTAlIiwgIjEwLTE1JSIsICIxNS0yMCUiLCAiPjIwJSIpKQ0KDQojIHN0YWNrZWQgYmFyIHBsb3Qgb2YgZGlmZmVyZW50IGN1dCBwb2ludHMgKDElLCA1JSwgMTAlLCAxNSUpDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gRGV0ZWN0aW9uVGhyZXNob2xkKSkgKw0KICBnZW9tX2JhcihhZXMoZmlsbCA9IEFOTjIpKSArDQogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgdmp1c3QgPSAtMC41KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMSkpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgbGFicyh4ID0gIkdlbmUgRGV0ZWN0aW9uIFJhdGUiLA0KICAgICAgIHkgPSAiU2VnbWVudHMsICMiLA0KICAgICAgIGZpbGwgPSAiU2VnbWVudCBUeXBlIikNCmBgYA0KDQpjdXQgcGVyY2VudCBnZW5lcyBkZXRlY3RlZCBhdCAxLCA1LCAxMCwgMTUNCg0KYGBge3IgY3V0X3RvX3BlcmNlbnR9DQprYWJsZSh0YWJsZShwRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uVGhyZXNob2xkLA0KICAgICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIpKQ0KDQojIHNldCB0aHJlc2hvbGQgZm9yIGRldGVjdGlvbmxldmVsDQojIGRlZmF1bHQgMC4xDQpnZW5lX2RldF90aHJlc2hvbGQgPC0gMC4wNQ0KDQp0YXJnZXRfRGF0YSA8LQ0KICB0YXJnZXRfRGF0YVssIHBEYXRhKHRhcmdldF9EYXRhKSRHZW5lRGV0ZWN0aW9uUmF0ZSA+PSBnZW5lX2RldF90aHJlc2hvbGRdDQoNCmRpbSh0YXJnZXRfRGF0YSkNCmBgYA0KDQojIDQuNiBNYW51YWwgcmVtb3ZhbCBvZiBzYW1wbGVzL2NsYXNzZXMNCg0KYGBge3IgcmVtb3ZlX3NhbXBsZXN9DQphY3RpdmVfYW9pczwtbmFtZXMoYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJleHBycyIpKSkNCmBgYA0KDQpyZS1Db2xsZWN0IGFubm90YXRpb25zDQoNCmBgYHtyIGNvbGxlY3RfYW5ub3RhdGlvbnN9DQojIGdhdGhlciB0aGUgZGF0YSBhbmQgcGxvdCBpbiBvcmRlcjogY2xhc3MsIHNsaWRlIG5hbWUsIHJlZ2lvbiwgc2VnbWVudA0KI2NvdW50X21hdCA8LSBkcGx5cjo6Y291bnQocERhdGEoRGF0YSksIEFOTjEsQU5OMixBTk4zLEFOTjQsc2xpZGVfbmFtZSkNCg0KdGVtcF9xYyA8LSB0ZW1wX2RmDQp0ZW1wX3FjJFFDUmVzdWx0IDwtIFFDUmVzdWx0cyRRQ1N0YXR1cw0KdGVtcF9xYyRRQ1Jlc3VsdFt0ZW1wX3FjJFFDUmVzdWx0ID09ICJXQVJOSU5HIl0gPC0gIlgiDQoNCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KGEsIEFOTjEsIEFOTjIsIHNsaWRlX25hbWUsIFFDUmVzdWx0KQ0KY291bnRfbWF0IDwtIHRlbXBfcWMgJT4lIGRwbHlyOjpjb3VudCh0ZW1wX3FjW2MoYW5uX25hbWVzLCAiUUNSZXN1bHQiKV0pDQoNCnRlc3RfZ3IgPC0gZ2F0aGVyX3NldF9kYXRhKGNvdW50X21hdCwgMTooYW5uX3NpemUrMSkpDQp0ZXN0X2dyJHggPC0gZmFjdG9yKHRlc3RfZ3IkeCwgbGFiZWxzID0gYyhhbm5fbmFtZXMsICJRQ1Jlc3VsdCIpKQ0KYGBgDQoNCnJlLVBsb3QgU2Fua2V5DQoNCmBgYHtyIHBsb3Rfc2Fua2V5LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMX0NCmdncGxvdCh0ZXN0X2dyLCBhZXMoeCwgaWQgPSBpZCwgc3BsaXQgPSB5LCB2YWx1ZSA9IG4pKSArDQogIGdlb21fcGFyYWxsZWxfc2V0cyhhZXMoZmlsbCA9IEFOTjIpLCBhbHBoYSA9IDAuNSwgYXhpcy53aWR0aCA9IDAuMSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfYXhlcyhheGlzLndpZHRoID0gMC4yKSArDQogIGdlb21fcGFyYWxsZWxfc2V0c19sYWJlbHMoY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSkgKw0KICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE3KSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oMCkpICsgDQogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKw0KICBhbm5vdGF0ZShnZW9tID0gInNlZ21lbnQiLCB4ID0gMy4yNSwgeGVuZCA9IDMuMjUsIHkgPSAxMCwgDQogICAgICAgICAgIHllbmQgPSA2MCwgbHdkID0gMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gMy4xOSwgeSA9IDI1LCBhbmdsZSA9IDkwLCBzaXplID0gNSwNCiAgICAgICAgICAgaGp1c3QgPSAwLjUsIGxhYmVsID0gIjUwIHNlZ21lbnRzIikNCmBgYA0KDQojIDQuNyBHZW5lIERldGVjdGlvbiBSYXRlDQoNCkNhbGN1bGF0ZSBkZXRlY3Rpb24gcmF0ZQ0KDQpgYGB7ciBjbGNfZGV0ZWN0aW9uX3JhdGV9DQpMT1FfTWF0IDwtIExPUV9NYXRbLCBjb2xuYW1lcyh0YXJnZXRfRGF0YSldDQpmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0ZWRTZWdtZW50cyA8LSByb3dTdW1zKExPUV9NYXQsIG5hLnJtID0gVFJVRSkNCmZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3Rpb25SYXRlIDwtDQogIGZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3RlZFNlZ21lbnRzIC8gbnJvdyhwRGF0YSh0YXJnZXRfRGF0YSkpDQpgYGANCg0KR2VuZSBvZiBpbnRlcmVzdCBkZXRlY3Rpb24gdGFibGUNCg0KYGBge3IgZ2VuZV9vZl9pbnRlcmVzdF90YWJsZX0NCmdvaSA8LSBjKCJQRENEMSIsICJDRDI3NCIsICJJRk5HIiwgIkNEOEEiLCAiQ0Q2OCIsICJFUENBTSIsDQogICAgICAgICAiS1JUMTgiLCAiTlBIUzEiLCAiTlBIUzIiLCAiQ0FMQjEiLCAiQ0xETjgiKQ0KZ29pX2RmIDwtIGRhdGEuZnJhbWUoDQogIEdlbmUgPSBnb2ksDQogIE51bWJlciA9IGZEYXRhKHRhcmdldF9EYXRhKVtnb2ksICJEZXRlY3RlZFNlZ21lbnRzIl0sDQogIERldGVjdGlvblJhdGUgPSBwZXJjZW50KGZEYXRhKHRhcmdldF9EYXRhKVtnb2ksICJEZXRlY3Rpb25SYXRlIl0pKQ0KYGBgDQoNCiMgNC44IEdlbmUgRmlsdGVyaW5nDQoNCldlIHdpbGwgZ3JhcGggdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCBpbiBkaWZmZXJlbnQNCnBlcmNlbnRhZ2VzIG9mIHNlZ21lbnRzLiBCYXNlZCBvbiB0aGUgdmlzdWFsaXphdGlvbiBiZWxvdywgd2UgY2FuIGJldHRlcg0KdW5kZXJzdGFuZCBnbG9iYWwgZ2VuZSBkZXRlY3Rpb24gaW4gb3VyIHN0dWR5IGFuZCBzZWxlY3QgaG93IG1hbnkgbG93DQpkZXRlY3RlZCBnZW5lcyB0byBmaWx0ZXIgb3V0IG9mIHRoZSBkYXRhc2V0LiBHZW5lIGZpbHRlcmluZyBpbmNyZWFzZXMNCnBlcmZvcm1hbmNlIG9mIGRvd25zdHJlYW0gc3RhdGlzdGljYWwgdGVzdHMgYW5kIGltcHJvdmVzIGludGVycHJldGF0aW9uDQpvZiB0cnVlIGJpb2xvZ2ljYWwgc2lnbmFsLg0KDQpQbG90IGRldGVjdGlvbiByYXRlDQoNCmBgYHtyIHBsb3RfZGV0X3JhdGV9DQpwbG90X2RldGVjdCA8LSBkYXRhLmZyYW1lKEZyZXEgPSBjKDEsIDUsIDEwLCAyMCwgMzAsIDUwKSkNCnBsb3RfZGV0ZWN0JE51bWJlciA8LQ0KICB1bmxpc3QobGFwcGx5KGMoMC4wMSwgMC4wNSwgMC4xLCAwLjIsIDAuMywgMC41KSwNCiAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7c3VtKGZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3Rpb25SYXRlID49IHgpfSkpDQpwbG90X2RldGVjdCRSYXRlIDwtIHBsb3RfZGV0ZWN0JE51bWJlciAvIG5yb3coZkRhdGEodGFyZ2V0X0RhdGEpKQ0Kcm93bmFtZXMocGxvdF9kZXRlY3QpIDwtIHBsb3RfZGV0ZWN0JEZyZXENCg0KZ2dwbG90KHBsb3RfZGV0ZWN0LCBhZXMoeCA9IGFzLmZhY3RvcihGcmVxKSwgeSA9IFJhdGUsIGZpbGwgPSBSYXRlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZm9ybWF0QyhOdW1iZXIsIGZvcm1hdCA9ICJkIiwgYmlnLm1hcmsgPSAiLCIpKSwNCiAgICAgICAgICAgIHZqdXN0ID0gMS42LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJvcmFuZ2UyIiwgbWlkID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiZG9kZ2VyYmx1ZTMiLCBtaWRwb2ludCA9IDAuNjUsDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBsaW1pdHMgPSBjKDAsMSksDQogICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMCkpKSArDQogIGxhYnMoeCA9ICIlIG9mIFNlZ21lbnRzIiwNCiAgICAgICB5ID0gIkdlbmVzIERldGVjdGVkLCAlIG9mIFBhbmVsID4gTE9RIikNCmBgYA0KDQpTdWJzZXQgdG8gdGFyZ2V0IGdlbmVzIGRldGVjdGVkIGluIGF0IGxlYXN0IDEwJSBvZiB0aGUgc2FtcGxlcy4gQWxzbw0KbWFudWFsbHkgaW5jbHVkZSB0aGUgbmVnYXRpdmUgY29udHJvbCBwcm9iZSwgZm9yIGRvd25zdHJlYW0gdXNlDQoNCmBgYHtyIHN1YnNldF90b18xMHBfZGV0ZWN0ZWRfZ2VuZXN9DQojIGRlZmF1bHQ9MC4xDQpuZWdhdGl2ZVByb2JlZkRhdGEgPC0gc3Vic2V0KGZEYXRhKHRhcmdldF9EYXRhKSwgQ29kZUNsYXNzID09ICJOZWdhdGl2ZSIpDQpuZWdfcHJvYmVzIDwtIHVuaXF1ZShuZWdhdGl2ZVByb2JlZkRhdGEkVGFyZ2V0TmFtZSkNCnRhcmdldF9EYXRhIDwtIA0KICB0YXJnZXRfRGF0YVtmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA+PSAwLjA1IHwNCiAgICAgICAgICAgICAgICAgICAgZkRhdGEodGFyZ2V0X0RhdGEpJFRhcmdldE5hbWUgJWluJSBuZWdfcHJvYmVzLCBdDQoNCiMgcmV0YWluIG9ubHkgZGV0ZWN0ZWQgZ2VuZXMgb2YgaW50ZXJlc3QNCmdvaSA8LSBnb2lbZ29pICVpbiUgcm93bmFtZXModGFyZ2V0X0RhdGEpXQ0KDQpkaW0odGFyZ2V0X0RhdGEpDQpgYGANCg0KIyA1IE5vcm1hbGl6YXRpb24NCg0KV2Ugd2lsbCBub3cgbm9ybWFsaXplIHRoZSBHZW9NeCBkYXRhIGZvciBkb3duc3RyZWFtIHZpc3VhbGl6YXRpb25zIGFuZA0KZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIFRoZSB0d28gY29tbW9uIG1ldGhvZHMgZm9yIG5vcm1hbGl6YXRpb24gb2YNCkRTUC1OR1MgUk5BIGRhdGEgYXJlIGkpIHF1YXJ0aWxlIDMgKFEzKSBvciBpaSkgYmFja2dyb3VuZCBub3JtYWxpemF0aW9uLg0KDQpCb3RoIG9mIHRoZXNlIG5vcm1hbGl6YXRpb24gbWV0aG9kcyBlc3RpbWF0ZSBhIG5vcm1hbGl6YXRpb24gZmFjdG9yIHBlcg0Kc2VnbWVudCB0byBicmluZyB0aGUgc2VnbWVudCBkYXRhIGRpc3RyaWJ1dGlvbnMgdG9nZXRoZXIuIE1vcmUgYWR2YW5jZWQNCm1ldGhvZHMgZm9yIG5vcm1hbGl6YXRpb24gYW5kIG1vZGVsaW5nIGFyZSB1bmRlciBhY3RpdmUgZGV2ZWxvcG1lbnQuDQpIb3dldmVyLCBmb3IgbW9zdCBzdHVkaWVzLCB0aGVzZSBtZXRob2RzIGFyZSBzdWZmaWNpZW50IGZvcg0KdW5kZXJzdGFuZGluZyBkaWZmZXJlbmNlcyBiZXR3ZWVuIGJpb2xvZ2ljYWwgY2xhc3NlcyBvZiBzZWdtZW50cyBhbmQNCnNhbXBsZXMuDQoNClEzIG5vcm1hbGl6YXRpb24gaXMgdHlwaWNhbGx5IHRoZSBwcmVmZXJyZWQgbm9ybWFsaXphdGlvbiBzdHJhdGVneSBmb3INCm1vc3QgRFNQLU5HUyBSTkEgc3R1ZGllcy4gR2l2ZW4gdGhlIGxvdyBuZWdhdGl2ZSBwcm9iZSBjb3VudHMgaW4gdGhpcw0KcGFydGljdWxhciBkYXRhc2V0IGFzIHNob3duIGR1cmluZyBTZWdtZW50IFFDLCB3ZSB3b3VsZCBmdXJ0aGVyIGF2b2lkDQpiYWNrZ3JvdW5kIG5vcm1hbGl6YXRpb24gYXMgaXQgbWF5IGJlIGxlc3Mgc3RhYmxlLg0KDQpCZWZvcmUgbm9ybWFsaXphdGlvbiwgd2Ugd2lsbCBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdXBwZXINCnF1YXJ0aWxlIChRMykgb2YgdGhlIGNvdW50cyBpbiBlYWNoIHNlZ21lbnQgd2l0aCB0aGUgZ2VvbWV0cmljIG1lYW4gb2YNCnRoZSBuZWdhdGl2ZSBjb250cm9sIHByb2JlcyBpbiB0aGUgZGF0YS4gSWRlYWxseSwgdGhlcmUgc2hvdWxkIGJlIGENCnNlcGFyYXRpb24gYmV0d2VlbiB0aGVzZSB0d28gdmFsdWVzIHRvIGVuc3VyZSB3ZSBoYXZlIHN0YWJsZSBtZWFzdXJlIG9mDQpRMyBzaWduYWwuIElmIHlvdSBkbyBub3Qgc2VlIHN1ZmZpY2llbnQgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZXNlIHZhbHVlcywNCnlvdSBtYXkgY29uc2lkZXIgbW9yZSBhZ2dyZXNzaXZlIGZpbHRlcmluZyBvZiBsb3cgc2lnbmFsIHNlZ21lbnRzL2dlbmVzLg0KDQpHcmFwaCBRMyB2YWx1ZSB2cyBuZWdHZW9NZWFuIG9mIE5lZ2F0aXZlcw0KDQpgYGB7ciBscG90X3EzX25lZ0dlb01lYW59DQphbm5fb2ZfaW50ZXJlc3QgPC0gIkFOTjIiDQpTdGF0X2RhdGEgPC0gDQogIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMoZXhwcnModGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICBTZWdtZW50ID0gY29sbmFtZXMoZXhwcnModGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICBBbm5vdGF0aW9uID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX29mX2ludGVyZXN0XSwNCiAgICAgICAgICAgICBRMyA9IHVubGlzdChhcHBseShleHBycyh0YXJnZXRfRGF0YSksIDIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUsIDAuNzUsIG5hLnJtID0gVFJVRSkpLA0KICAgICAgICAgICAgIE5lZ1Byb2JlID0gZXhwcnModGFyZ2V0X0RhdGEpW25lZ19wcm9iZXMsIF0pDQpTdGF0X2RhdGFfbSA8LSBtZWx0KFN0YXRfZGF0YSwgbWVhc3VyZS52YXJzID0gYygiUTMiLCAiTmVnUHJvYmUiKSwNCiAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTdGF0aXN0aWMiLCB2YWx1ZS5uYW1lID0gIlZhbHVlIikNCg0KcGx0MSA8LSBnZ3Bsb3QoU3RhdF9kYXRhX20sDQogICAgICAgICAgICAgICBhZXMoeCA9IFZhbHVlLCBmaWxsID0gU3RhdGlzdGljKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKw0KICBmYWNldF93cmFwKH5Bbm5vdGF0aW9uLCBucm93ID0gMSkgKyANCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9IDMsIHR5cGUgPSAicXVhbCIpICsNCiAgbGFicyh4ID0gIkNvdW50cyIsIHkgPSAiU2VnbWVudHMsICMiKQ0KDQpwbHQyIDwtIGdncGxvdChTdGF0X2RhdGEsDQogICAgICAgICAgICAgICBhZXMoeCA9IE5lZ1Byb2JlLCB5ID0gUTMsIGNvbG9yID0gQW5ub3RhdGlvbikpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsdHkgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya2dyYXkiKSArDQogIGdlb21fcG9pbnQoKSArIGd1aWRlcyhjb2xvciA9ICJub25lIikgKyB0aGVtZV9idygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSAiTmVnYXRpdmUgUHJvYmUgR2VvTWVhbiwgQ291bnRzIiwgeSA9ICJRMyBWYWx1ZSwgQ291bnRzIikNCg0KcGx0MyA8LSBnZ3Bsb3QoU3RhdF9kYXRhLA0KICAgICAgICAgICAgICAgYWVzKHggPSBOZWdQcm9iZSwgeSA9IFEzIC8gTmVnUHJvYmUsIGNvbG9yID0gQW5ub3RhdGlvbikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtncmF5IikgKw0KICBnZW9tX3BvaW50KCkgKyB0aGVtZV9idygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSAiTmVnYXRpdmUgUHJvYmUgR2VvTWVhbiwgQ291bnRzIiwgeSA9ICJRMy9OZWdQcm9iZSBWYWx1ZSwgQ291bnRzIikNCg0KYnRtX3JvdyA8LSBwbG90X2dyaWQocGx0MiwgcGx0MywgbnJvdyA9IDEsIGxhYmVscyA9IGMoIkIiLCAiIiksDQogICAgICAgICAgICAgICAgICAgICByZWxfd2lkdGhzID0gYygwLjQzLDAuNTcpKQ0KcGxvdF9ncmlkKHBsdDEsIGJ0bV9yb3csIG5jb2wgPSAxLCBsYWJlbHMgPSBjKCJBIiwgIiIpKQ0KDQpgYGANCg0KUTMgbm9ybSAoNzV0aCBwZXJjZW50aWxlKSBmb3IgV1RBL0NUQSB3aXRoIG9yIHdpdGhvdXQgY3VzdG9tIHNwaWtlLWlucw0KDQpgYGB7ciBxM19ub3JtfQ0KdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaXJlZFF1YW50aWxlID0gLjc1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b0VsdCA9ICJxX25vcm0iKQ0KIywgZGF0YV90eXBlID0gIlJOQSIgZGVwcmljYXRlZCBhZnRlciA0LjENCg0KYGBgDQoNClF1YW50aWxlIE5vcm1hbGl6YXRpb24NCg0KYGBge3IgcXVhbnRpbGV9DQpxdWFudGlsZSA8LSBub3JtYWxpemUucXVhbnRpbGVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0Kcm93bmFtZXMocXVhbnRpbGUpIDwtIHJvd25hbWVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KY29sbmFtZXMocXVhbnRpbGUpIDwtIGNvbG5hbWVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicXVhbnRpbGVfbm9ybSIpIDwtICBxdWFudGlsZQ0KYGBgDQoNCkJhY2tncm91bmQgbm9ybWFsaXphdGlvbiBmb3IgV1RBL0NUQSB3aXRob3V0IGN1c3RvbSBzcGlrZS1pbg0KDQpgYGB7ciBiZ19ub3JtfQ0KdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX21ldGhvZCA9ICJuZWciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbUVsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvRWx0ID0gIm5lZ19ub3JtIikNCg0KIyAsIGRhdGFfdHlwZSA9ICJSTkEiIGRlcHJpY2F0ZWQgYWZ0ZXIgNC4xDQpgYGANCg0KIyA1LjEgVmlzdWFsaXplIHRoZSBmaXJzdCAxMCBzZWdtZW50cyB3aXRoIGVhY2ggbm9ybWFsaXphdGlvbiBtZXRob2Qgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KYGBge3IgdmlzdWxhaXplX25vcm1zfQ0KDQojRml4IHplcm8gdmFsdWVzLCB3aGljaCBnbyB0byAtaW5mIGluIGxvZyB0cmFuc2Zvcm0gaW4gc3RhbmRhcmQgYm94cGxvdA0KIyB0ZW1wIDwtYXMubWF0cml4KGV4cHJzKCh0YXJnZXRfRGF0YSlbLDE6MTBdKSkNCiMgbG9uZyA8LSBtZWx0KHRlbXApDQojIGNvbG5hbWVzKGxvbmcpIDwtIGMoImdlbmUiLCJzZWdtZW50IiwiY291bnQiKQ0KIyBnZ3Bsb3QobG9uZywgYWVzKHg9c2VnbWVudCx5PWNvdW50KSkgKw0KIyAgICAgZ2VvbV9ib3hwbG90KGZpbGw9IiM5RURBRTUiKSArDQojICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9c2NhbGVzOjpwc2V1ZG9fbG9nX3RyYW5zKGJhc2UgPSAxMCkpICsNCiMgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoMToxMCkpICsNCiMgICAgIGxhYnModGl0bGU9IlJhdyBjb3VudHMiLCB4PSJzZWdtZW50IiwgeSA9ICJDb3VudHMsIFJhdyIpDQojIA0KIyANCiMgdGVtcCA8LWFzLm1hdHJpeChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjEwXSwgZWx0ID0gInFfbm9ybSIpKQ0KIyBsb25nIDwtIG1lbHQodGVtcCkNCiMgY29sbmFtZXMobG9uZykgPC0gYygiZ2VuZSIsInNlZ21lbnQiLCJjb3VudCIpDQojIGdncGxvdChsb25nLCBhZXMoeD1zZWdtZW50LHk9Y291bnQpKSArDQojICAgICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjMkNBMDJDIikgKw0KIyAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6cHNldWRvX2xvZ190cmFucyhiYXNlID0gMTApKSArDQojICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKDE6MTApKSArDQojICAgICBsYWJzKHRpdGxlPSJRMyBOb3JtIENvdW50cyIsIHg9InNlZ21lbnQiLCB5ID0gIkNvdW50cywgUTMgTm9ybWFsaXplZCIpDQojIA0KIyANCiMgdGVtcCA8LWFzLm1hdHJpeChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjEwXSwgZWx0ID0gIm5lZ19ub3JtIikpDQojIGxvbmcgPC0gbWVsdCh0ZW1wKQ0KIyBjb2xuYW1lcyhsb25nKSA8LSBjKCJnZW5lIiwic2VnbWVudCIsImNvdW50IikNCiMgZ2dwbG90KGxvbmcsIGFlcyh4PXNlZ21lbnQseT1jb3VudCkpICsNCiMgICAgIGdlb21fYm94cGxvdChmaWxsID0gIiNGRjdGMEUiKSArDQojICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9c2NhbGVzOjpwc2V1ZG9fbG9nX3RyYW5zKGJhc2UgPSAxMCkpICsNCiMgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoMToxMCkpICsNCiMgICAgIGxhYnModGl0bGU9Ik5lZyBOb3JtIENvdW50cyIsIHg9InNlZ21lbnQiLCB5ID0gIkNvdW50cywgTmVnLiBOb3JtYWxpemVkIikNCmBgYA0KDQojIyByYXcgY291bnRzDQoNCmBgYHtyfQ0KYm94cGxvdChleHBycyh0YXJnZXRfRGF0YSlbLDE6OF0sDQogICAgICAgIGNvbCA9ICIjOUVEQUU1IiwgbWFpbiA9ICJSYXcgQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFJhdyIpDQpgYGANCg0KIyMgUTMgbm9ybWFsaXplZCB7LmFjdGl2ZX0NCg0KYGBge3J9DQpib3hwbG90KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6OF0sIGVsdCA9ICJxX25vcm0iKSwNCiAgICAgICAgY29sID0gIiMyQ0EwMkMiLCBtYWluID0gIlEzIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFEzIE5vcm1hbGl6ZWQiKQ0KYGBgDQoNCiMjIFF1YW50aWxlIG5vcm1hbGl6ZWQgKHRlc3RpbmcgcGhhc2UpDQoNCmBgYHtyfQ0KYm94cGxvdChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjhdLCBlbHQgPSAicXVhbnRpbGVfbm9ybSIpLA0KICAgICAgICBjb2wgPSAicGluayIsIG1haW4gPSAiUXVhbnRpbGUgTm9ybSBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgUXVhbnRpbGUgTm9ybWFsaXplZCIpDQpgYGANCg0KIyMgTmVnYXRpdmUgcHJvYmUgbm9ybWFsaXphdGlvbg0KDQpgYGB7cn0NCmJveHBsb3QoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMTo4XSwgZWx0ID0gIm5lZ19ub3JtIiksDQogICAgICAgIGNvbCA9ICIjRkY3RjBFIiwgbWFpbiA9ICJOZWcgTm9ybSBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgTmVnLiBOb3JtYWxpemVkIikNCg0KYGBgDQoNCiMgNiBVbnN1cGVydmlzZWQgQW5hbHlzaXMNCg0KIyA2LjEgVU1BUCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpVc2UgdGhlIHRhYi1tZW51IHRvIG5hdmlnYXRlIQ0KDQojIyAxDQoNCmBgYHtyIHVtYXAsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQpjdXN0b21fdW1hcCA8LSB1bWFwOjp1bWFwLmRlZmF1bHRzDQpjdXN0b21fdW1hcCRyYW5kb21fc3RhdGUgPC0gNDINCiMgcnVuIFVNQVANCg0KdW1hcF9vdXQgPC0NCiAgdW1hcCh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgIGNvbmZpZyA9IGN1c3RvbV91bWFwKQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygiVU1BUDEiLCAiVU1BUDIiKV0gPC0gdW1hcF9vdXQkbGF5b3V0WywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSBBTk4xKSkgKw0KDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KDQpiYXRjaF9jb3JyZWN0aW9uX25lZWRlZCA9IEZBTFNFDQpgYGANCg0KIyMgMg0KDQpgYGB7ciB1bWFwMiwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9OH0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IEFOTjMsIHNoYXBlID0gQU5OMSkpICsNCg0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIDYuMiBCYXRjaCBjb3JyZWN0aW9uIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCklmIHRoZSBkYXRhIGlzIG9ic2VydmVkIHRvIGhhdmUgYSBiYXRjaCBlZmZlY3QgaXQgY2FuIGJlIGNvcnJlY3RlZCB3aXRoIHRoZSBtZXRob2RzOiBSVVY0LCBMSU1NQSwgb3IgQ29tYmF0LVNlcS4gRWFjaCBtZXRob2QgaXMgZG9uZSBhbmQgdGhlIGJlc3Qgb25lIGlzIHBpY2tlZCBhbmQgdXNlZC4NCg0KVGhlIHN0YW5kUiBwYWNrYWdlIGlzIHNob3J0IGZvciBTcGF0aWFsIHRyYW5zY3JpcHRvbWljcyBhbmFseXplcyBhbmQgZGVjb2RpbmcgaW4gUiwgaXQgYWltcyBhdCBwcm92aWRpbmcgZ29vZCBwcmFjdGljZSBwaXBlbGluZSBhbmQgdXNlZnVsIGZ1bmN0aW9ucyBmb3IgdXNlcnMgdG8gYW5hbHl6ZSBOYW5vc3RyaW5n4oCZcyBHZW9NeCBEU1AgZGF0YS4gSW4gdGhlIE5hbm9zdHJpbmfigJlzIEdlb01YIERTUCBwcm90b2NvbCwgZHVlIHRvIHRoZSBmYWN0IHRoYXQgb25lIHNsaWRlIGlzIG9ubHkgYmlnIGVub3VnaCBmb3IgYSBoYW5kZnVsIG9mIHRpc3N1ZSBzZWdtZW50cyAoUk9JcyksIGl0IGlzIGNvbW1vbiB0aGF0IHdlIHNlZSB0aGUgRFNQIGRhdGEgYmVpbmcgY29uZm91bmRlZCBieSB0aGUgYmF0Y2ggZWZmZWN0IGludHJvZHVjZWQgYnkgZGlmZmVyZW50IHNsaWRlcy4gSW4gb3JkZXIgdG8gZXN0YWJsaXNoIGZhaXIgY29tcGFyaXNvbiBiZXR3ZWVuIFJPSXMgbGF0ZXIgb24sIGl0IGlzIG5lY2Vzc2FyeSB0byByZW1vdmUgdGhpcyBiYXRjaCBlZmZlY3QgZnJvbSB0aGUgZGF0YS4gKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3N0YW5kUi9pbnN0L2RvYy9zdGFuZFJfaW50cm9kdWN0aW9uLmh0bWwpDQoNCkZvciBSVVY0IGNvcnJlY3Rpb24sIHRoZSBmdW5jdGlvbiBpcyByZXF1aXJpbmcgMyBwYXJhbWV0ZXJzIG90aGVyIHRoYW4gdGhlIGlucHV0IG9iamVjdCwgaW5jbHVkaW5nIGZhY3RvcnM6IHRoZSBmYWN0b3Igb2YgaW50ZXJlc3QsIGkuZS4gdGhlIGJpb2xvZ2ljYWwgdmFyaWF0aW9uIHdlIHBsYW4gdG8ga2VlcDsgTkNHczogdGhlIGxpc3Qgb2YgbmVnYXRpdmUgY29udHJvbCBnZW5lcyBkZXRlY3RlZCB1c2luZyB0aGUgZnVuY3Rpb24gZmluZE5DR3M7IGFuZCBrOiBpcyB0aGUgbnVtYmVyIG9mIHVud2FudGVkIGZhY3RvcnMgdG8gdXNlLCBpbiB0aGUgUlVWIGRvY3VtZW50YXRpb24sIGl0IGlzIHN1Z2dlc3QgdGhhdCB3ZSBzaG91bGQgdXNlIHRoZSBzbWFsbGVzdCBrIG9uY2Ugd2UgZG9u4oCZdCBvYnNlcnZlIHRlY2huaWNhbCB2YXJpYXRpb24gaW4gdGhlIGRhdGEuDQoNCkFub3RoZXIgb3B0aW9uIGlzIHNldCB0aGUgcGFyYW1ldGVyIG1ldGhvZCB0byDigJxMaW1tYeKAnSwgd2hpY2ggdXNlcyB0aGUgcmVtb3ZlIGJhdGNoIGNvcnJlY3Rpb24gbWV0aG9kIGZyb20gbGltbWEuIEluIHRoaXMgbW9kZSwgdGhlIGZ1bmN0aW9uIGlzIHJlcXVpcmluZyAyIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBiYXRjaDogYSB2ZWN0b3IgdGhhdCBpbmRpY2F0aW5nIGJhdGNoZXMgZm9yIGFsbCBzYW1wbGVzOyBhbmQgZGVzaWduOiBhIGRlc2lnbiBtYXRyaXggd2hpY2ggaXMgZ2VuZXJhdGVkIGJ5IG1vZGVsLm1hdHJpeCwgaW4gdGhlIGRlc2lnbiBtYXRyaXgsIGFsbCBiaW9sb2dpY2FsbHktcmVsZXZhbnQgZmFjdG9ycyBzaG91bGQgYmUgaW5jbHVkZWQuDQoNCkNvbUJhdC1zZXEgaXMgYSBiYXRjaCBlZmZlY3QgYWRqdXN0bWVudCB0b29sIGZvciBidWxrIFJOQS1zZXEgY291bnQgZGF0YS4gSXQgaXMgYW4gaW1wcm92ZWQgbW9kZWwgYmFzZWQgb24gdGhlIHBvcHVsYXIgQ29tQmF0LCB0byBhZGRyZXNzIGl0cyBsaW1pdGF0aW9ucyB0aHJvdWdoIG5vdmVsIG1ldGhvZHMgZGVzaWduZWQgc3BlY2lmaWNhbGx5IGZvciBSTkEtU2VxIHN0dWRpZXMuIENvbUJhdC1zZXEgdGFrZXMgdW50cmFuc2Zvcm1lZCwgcmF3IGNvdW50IG1hdHJpeCBhcyBpbnB1dC4gU2FtZSBhcyBDb21CYXQsIGl0IHJlcXVpcmVzIGEga25vd24gYmF0Y2ggdmFyaWFibGUuIChodHRwczovL2dpdGh1Yi5jb20vemhhbmd5dXFpbmcvQ29tQmF0LXNlcSkNCg0KIyMgU2V0IHVwIHN0YW5kUiBvYmplY3QNCg0KYGBge3IgYmF0Y2ggY29ycmVjdGlvbiwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCmNvdW50X2dlb214IDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1siZXhwcnMiXV0pDQoNCnNhbXBsZV9nZW9teCA8LSB0YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YQ0Kc2FtcGxlX2dlb214JHJvaSA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YSRyb2kNCnNhbXBsZV9nZW9teCA8LSBhcy5kYXRhLmZyYW1lKHNhbXBsZV9nZW9teCkNCnNhbXBsZV9nZW9teCRTYW1wbGVfSUQgPC0gcm93bmFtZXMoc2FtcGxlX2dlb214KQ0Kc2FtcGxlX2dlb214JFNlZ21lbnREaXNwbGF5TmFtZSA8LSBwYXN0ZShzYW1wbGVfZ2VvbXgkYHNjYW4gbmFtZWAsIHNhbXBsZV9nZW9teCRyb2ksIHNhbXBsZV9nZW9teCRzZWdtZW50LCBzZXAgPSAiIHwgIikNCnNhbXBsZV9nZW9teCRST0lDb29yZGluYXRlWCA8LSAxDQpzYW1wbGVfZ2VvbXgkUk9JQ29vcmRpbmF0ZVkgPC0gMQ0KDQoNCmZlYXR1cmVfZ2VvbXggPC0gZkRhdGEoRGF0YSkNCmZlYXR1cmVfZ2VvbXggPC0gZmVhdHVyZV9nZW9teFtjKCJSVFNfSUQiLCAiVGFyZ2V0TmFtZSIsICJQcm9iZUlEIiwgIk5lZ2F0aXZlIildDQpmZWF0dXJlX2dlb214IDwtIGFzLmRhdGEuZnJhbWUoZmVhdHVyZV9nZW9teCkNCnJvd25hbWVzKGZlYXR1cmVfZ2VvbXgpIDwtIE5VTEwNCg0KbWF0Y2hpbmcgPC0gc2FtcGxlX2dlb214JFNlZ21lbnREaXNwbGF5TmFtZVtzYW1wbGVfZ2VvbXgkU2FtcGxlX0lEICVpbiUgY29sbmFtZXMoY291bnRfZ2VvbXgpXQ0KY29sbmFtZXMoY291bnRfZ2VvbXgpIDwtIG1hdGNoaW5nDQpjb3VudF9nZW9teCRUYXJnZXROYW1lIDwtIHJvd25hbWVzKGNvdW50X2dlb214KQ0Kcm93bmFtZXMoY291bnRfZ2VvbXgpIDwtIE5VTEwNCg0Kc3BlIDwtIHJlYWRHZW9NeChjb3VudF9nZW9teCwgc2FtcGxlX2dlb214LCBmZWF0dXJlQW5ub0ZpbGUgPSBmZWF0dXJlX2dlb214LCBoYXNOZWdQcm9iZSA9IFRSVUUpDQoNCmNvbERhdGEoc3BlKSRyZWdpb25zIDwtIHBhc3RlMChjb2xEYXRhKHNwZSkkQU5OMiwiXyIsY29sRGF0YShzcGUpJEFOTjEpIHw+IA0KICAoXCguKSBnc3ViKCJfR2VvbWV0cmljIFNlZ21lbnQiLCIiLC4pKSgpIHw+DQogIHBhc3RlMCgiXyIsY29sRGF0YShzcGUpJHBhdGhvbG9neSkgfD4NCiAgKFwoLikgZ3N1YigiX05BIiwiX25zIiwuKSkoKQ0KDQpjb2xEYXRhKHNwZSkkcmVnaW9ucyA8LSBwYXN0ZTAoY29sRGF0YShzcGUpJEFOTjIsIl8iLGNvbERhdGEoc3BlKSRBTk4xKSB8PiANCiAgKFwoLikgZ3N1YigiX0dlb21ldHJpYyBTZWdtZW50IiwiIiwuKSkoKSB8Pg0KICBwYXN0ZTAoIl8iLGNvbERhdGEoc3BlKSRwYXRob2xvZ3kpIHw+DQogIChcKC4pIGdzdWIoIl9OQSIsIl9ucyIsLikpKCkNCmNvbERhdGEoc3BlKSRiaW9sb2d5IDwtIHBhc3RlMChjb2xEYXRhKHNwZSkkcmVnaW9ucykNCmBgYA0KDQojIyBTZWUgb3B0aW1hbCBrIHZhbHVlIGZvciBSVVY0DQoNCmBgYHtyLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0Kc3BlIDwtIGZpbmROQ0dzKHNwZSwgYmF0Y2hfbmFtZSA9ICJzbGlkZV9uYW1lIiwgdG9wX24gPSA1MDApDQpmaW5kQmVzdEsoc3BlLCBtYXhLID0gMTAsIGZhY3Rvcl9vZl9pbnQgPSAiYmlvbG9neSIsIE5DR3MgPSBtZXRhZGF0YShzcGUpJE5DR3MsIGZhY3Rvcl9iYXRjaCA9ICJzbGlkZV9uYW1lIikNCmBgYA0KDQojIyBSVVY0DQoNCmBgYHtyIHJ1djQgYmF0Y2gsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTEwLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KcnV2NCA8LSBnZW9teEJhdGNoQ29ycmVjdGlvbihzcGUsIGZhY3RvcnMgPSAiYmlvbG9neSIsIA0KICAgICAgICAgICAgICAgICAgIE5DR3MgPSBtZXRhZGF0YShzcGUpJE5DR3MsIGsgPSAxKQ0KDQpwbG90UGFpclBDQShydXY0LCBhc3NheSA9IDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSByZWdpb25zLCB0aXRsZSA9ICJSVVY0IHJlbW92ZUJhdGNoIikNCg0KcGxvdFJMRXhwcihydXY0LCBhc3NheSA9IDIsIGNvbG9yID0gYHNsaWRlX25hbWVgKSArIGdndGl0bGUoIlJVVjQgcmVtb3ZlQmF0Y2giKQ0KYGBgDQoNCiMjIExpbW1hDQoNCmBgYHtyIGxpbW1hIGJhdGNoLCAgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9MTAsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpsaW1tYSA8LSBnZW9teEJhdGNoQ29ycmVjdGlvbihzcGUsDQogICAgICAgICAgICAgICAgICAgICAgIGJhdGNoID0gY29sRGF0YShzcGUpJGBzbGlkZV9uYW1lYCwgbWV0aG9kID0gIkxpbW1hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gbW9kZWwubWF0cml4KH4gMCArIEFOTjEgKyByZWdpb25zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBjb2xEYXRhKHNwZSkpKQ0KDQpwbG90UGFpclBDQShsaW1tYSwgYXNzYXkgPSAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gcmVnaW9ucywgdGl0bGUgPSAiTGltbWEgcmVtb3ZlQmF0Y2giKQ0KDQpwbG90UkxFeHByKGxpbW1hLCBhc3NheSA9IDIsIGNvbG9yID0gc2xpZGVfbmFtZSkgKyBnZ3RpdGxlKCJMaW1tYSByZW1vdmVCYXRjaCIpDQpgYGANCg0KIyMgQ29tYmF0U2VxDQoNCmBgYHtyLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KYWRqdXN0ZWQgPC0gQ29tQmF0X3NlcSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSwgYmF0Y2g9dGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZV9uYW1lIl1dLCBncm91cD10YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YVtbIkFOTjIiXV0pDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAiY29tYmF0IikgPC0gIGFkanVzdGVkDQoNCnVtYXBfb3V0MiA8LQ0KICB1bWFwKHQobG9nMihhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gImNvbWJhdCIpKSksDQogICAgICAgY29uZmlnID0gY3VzdG9tX3VtYXApDQpwRGF0YSh0YXJnZXRfRGF0YSlbLCBjKCJVTUFQMSIsICJVTUFQMiIpXSA8LSB1bWFwX291dDIkbGF5b3V0WywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSBBTk4xKSkgKw0KDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKSArIGdndGl0bGUoIkNvbWJhdFNlcSByZW1vdmVCYXRjaCIpDQpgYGANCg0KIyMgQ29tcGFyZSBsaW1tYSB2cyBSVVY0DQoNCmBgYHtyIGNvbXBhcmUgYmF0Y2gsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpzcGVfbGlzdCA8LSBsaXN0KHNwZSwgcnV2NCwgbGltbWEpDQoNCnBsb3RDbHVzdGVyRXZhbFN0YXRzKHNwZV9saXN0ID0gc3BlX2xpc3QsDQogICAgICAgICAgICAgICAgICAgICBiaW9fZmVhdHVyZV9uYW1lID0gInJlZ2lvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgYmF0Y2hfZmVhdHVyZV9uYW1lID0gInNsaWRlX25hbWUiLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YV9uYW1lcyA9IGMoIlJhdyIsIlJVVjQiLCJMaW1tYSIpKQ0KYGBgDQoNCiMjIEFkZCBiYXRjaCBjb3JyZWN0aW9uIHJlc3VsdCB0byB0YXJnZXRfRGF0YQ0KDQpgYGB7ciBhZGQgYmF0Y2ggY29ycmVjdGlvbiwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCm5lZ19wcm9iZXNfc2F2ZSA8LSB0KGFzLm1hdHJpeCh0YXJnZXRfRGF0YUBhc3NheURhdGEkcV9ub3JtWyJOZWdQcm9iZS1XVFgiLF0pKQ0Kcm93bmFtZXMobmVnX3Byb2Jlc19zYXZlKSA8LSAiTmVnUHJvYmUtV1RYIg0KDQojIERlcGVuZGluZyBvbiBjb3JyZWN0IG1ldGhvZCwgY2hhbmdlIHRoZSB3b3JkICJsaW1tYSIgdG8gInJ1djQiIG9yIHZpY2UgdmVyc2EuDQpsaW1tYSA8LWxpbW1hQGFzc2F5c0BkYXRhQGxpc3REYXRhW1sibG9nY291bnRzIl1dDQpsaW1tYSA8LSByYmluZChsaW1tYSwgbmVnX3Byb2Jlc19zYXZlKQ0KY29sbmFtZXMobGltbWEpIDwtIGNvbG5hbWVzKGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKSkNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsaW1tYSIpIDwtICBsaW1tYQ0KDQpydXY0IDwtcnV2NEBhc3NheXNAZGF0YUBsaXN0RGF0YVtbImxvZ2NvdW50cyJdXQ0KcnV2NCA8LSByYmluZChydXY0LCBuZWdfcHJvYmVzX3NhdmUpDQpjb2xuYW1lcyhydXY0KSA8LSBjb2xuYW1lcyhhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbInFfbm9ybSJdXSkpDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicnV2NCIpIDwtICBydXY0DQpgYGANCg0KIyMgQ2hvb3NlIG1ldGhvZCB7LmFjdGl2ZX0NCg0KYGBge3J9DQojIGNob29zZSBtZXRob2QgdG8gcmVwbGFjZSBxX25vcm0gb3Igc2V0IGl0IHRvICIiDQptZXRob2QgPC0gIiINCg0KIyByZXBsYWNlIHFfbm9ybSB3aXRoIGNob3NlbiBtZXRob2QNCmlmICghbWV0aG9kID09ICIiKSB7DQogICMgc2F2ZSBvcmdpbmFsIHFfbm9ybQ0KICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAib3JpZ2luYWwiKSA8LSAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInFfbm9ybSIpDQogIA0KICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicV9ub3JtIikgPC0gIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9IG1ldGhvZCkNCiAgcHJpbnQocGFzdGUoIkJhdGNoIGNvcnJlY3Rpb24gbWV0aG9kOiIsIG1ldGhvZCkpDQp9IGVsc2Uge3ByaW50KCJObyBiYXRjaCBjb3JyZWN0aW9uIG5lZWRlZCIpfQ0KYGBgDQoNCiMjIFVtYXAgYWZ0ZXIgYmF0Y2ggY29ycmVjdGlvbg0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9OCwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnVtYXBfb3V0IDwtDQogIHVtYXAodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICBjb25maWcgPSBjdXN0b21fdW1hcCkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoIlVNQVAxIiwgIlVNQVAyIildIDwtIHVtYXBfb3V0JGxheW91dFssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gQU5OMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjMgUnVuIHRTTkUgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KT25lIGNvbW1vbiBhcHByb2FjaCB0byB1bmRlcnN0YW5kaW5nIGhpZ2gtcGxleCBkYXRhIGlzIGRpbWVuc2lvbg0KcmVkdWN0aW9uLiBUd28gY29tbW9uIG1ldGhvZHMgYXJlIFVNQVAgYW5kIHRTTkUsIHdoaWNoIGFyZQ0Kbm9uLW9ydGhvZ29uYWxseSBjb25zdHJhaW5lZCBwcm9qZWN0aW9ucyB0aGF0IGNsdXN0ZXIgc2FtcGxlcyBiYXNlZCBvbg0Kb3ZlcmFsbCBnZW5lIGV4cHJlc3Npb24uIEluIHRoaXMgc3R1ZHksIHdlIHNlZSBieSBlaXRoZXIgVU1BUCAoZnJvbSB0aGUNCnVtYXAgcGFja2FnZSkgb3IgdFNORSAoZnJvbSB0aGUgUnRzbmUgcGFja2FnZSkNCg0KIyMgMQ0KDQpgYGB7ciB0U05FLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KdHNuZV9vdXQgPC0NCiAgUnRzbmUodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICAgcGVycGxleGl0eSA9IG5jb2wodGFyZ2V0X0RhdGEpKi4xNSkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoInRTTkUxIiwgInRTTkUyIildIDwtIHRzbmVfb3V0JFlbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gdFNORTEsIHkgPSB0U05FMiwgc2hhcGUgPSBzbGlkZV9uYW1lLCBjb2xvciA9IEFOTjIpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMjIDINCg0KYGBge3IsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQp0c25lX291dCA8LQ0KICBSdHNuZSh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgICBwZXJwbGV4aXR5ID0gbmNvbCh0YXJnZXRfRGF0YSkqLjE1KQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygidFNORTEiLCAidFNORTIiKV0gPC0gdHNuZV9vdXQkWVssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSB0U05FMSwgeSA9IHRTTkUyLCBjb2xvciA9IEFOTjMsIHNoYXBlID0gQU5OMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjQgQ2x1c3RlcmluZyBoaWdoIENWIEdlbmVzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCkFub3RoZXIgYXBwcm9hY2ggdG8gZXhwbG9yZSB0aGUgZGF0YSBpcyB0byBjYWxjdWxhdGUgdGhlIGNvZWZmaWNpZW50IG9mDQp2YXJpYXRpb24gKCRDViQpIGZvciBlYWNoIGdlbmUgKCRnJCkgdXNpbmcgdGhlIGZvcm11bGENCiRDVl9nPVNEX2cvbWVhbl9nJC4gV2UgdGhlbiBpZGVudGlmeSBnZW5lcyB3aXRoIGhpZ2ggQ1ZzIHRoYXQgc2hvdWxkDQpoYXZlIGxhcmdlIGRpZmZlcmVuY2VzIGFjcm9zcyB0aGUgdmFyaW91cyBwcm9maWxlZCBzZWdtZW50cy4gVGhpcw0KdW5iaWFzZWQgYXBwcm9hY2ggY2FuIHJldmVhbCBoaWdobHkgdmFyaWFibGUgZ2VuZXMgYWNyb3NzIHRoZSBzdHVkeS4NCg0KV2UgcGxvdCB0aGUgcmVzdWx0cyB1c2luZyB1bnN1cGVydmlzZWQgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcsDQpkaXNwbGF5ZWQgYXMgYSBoZWF0bWFwLg0KDQpgYGB7ciBjbHVzdGVyaW5nMX0NCiMgY3JlYXRlIGEgbG9nMiB0cmFuc2Zvcm0gb2YgdGhlIGRhdGEgZm9yIGFuYWx5c2lzDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibG9nX3EiKSA8LQ0KICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQoNCiMgY3JlYXRlIENWIGZ1bmN0aW9uDQpjYWxjX0NWIDwtIGZ1bmN0aW9uKHgpIHtzZCh4KSAvIG1lYW4oeCl9DQpDVl9kYXQgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgZWx0ID0gImxvZ19xIiwgTUFSR0lOID0gMSwgY2FsY19DVikNCiMgc2hvdyB0aGUgaGlnaGVzdCBDRCBnZW5lcyBhbmQgdGhlaXIgQ1YgdmFsdWVzDQpzb3J0KENWX2RhdCwgZGVjcmVhc2luZyA9IFRSVUUpWzE6NV0NCmBgYA0KDQojIyBUYWJsZSBvZiBDViB2YWx1ZXMNCg0KYGBge3J9DQojIHNob3cgdGhlIGhpZ2hlc3QgQ0QgZ2VuZXMgYW5kIHRoZWlyIENWIHZhbHVlcw0KZGF0YXRhYmxlKGFzLmRhdGEuZnJhbWUoQ1ZfZGF0KSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBvcmRlciA9IGxpc3QoMSwgJ2Rlc2MnKSwNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKSwgY2FwdGlvbiA9ICJDViB2YWx1ZXMgb2YgZ2VuZXMiIA0KKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJDVl9kYXQiKSwgZGlnaXRzPTMpDQoNCmBgYA0KDQojIyBIZWF0bWFwIGdlbmVzIGluIHRoZSB0b3AgM3JkIG9mIHRoZSBDViB2YWx1ZXMgey5hY3RpdmV9DQoNCmBgYHtyIENWaGVhdG1hcCwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTV9DQpHT0kgPC0gbmFtZXMoQ1ZfZGF0KVtDVl9kYXQgPiBxdWFudGlsZShDVl9kYXQsIDAuNzUpXQ0KDQpwaGVhdG1hcChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhW0dPSSwgXSwgZWx0ID0gImxvZ19xIiksDQogICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgZHJvcF9sZXZlbHMgPSBUUlVFLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQoNCmBgYA0KDQpgYGB7ciBsb2dfdHJhbnNmb3JtfQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0gIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCmxvZ19xIDwtYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJsb2dfcSIpKQ0KI2JhdGNoIDwtYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJiYXRjaCIpKQ0KYGBgDQoNCiMgNi41LjAgQ3JlYXRlIHN1YnNldCBvZiBkYXRhDQoNCmBgYHtyIGRlZmluZV9hY3RpdmVfYW9pc30NCiMgZGV0ZXJtaW5lIEFPSXMgdG8gdXNlDQojYWN0aXZlX2FvaXM8LXJvd25hbWVzKGFubilbYW5uJHBhdGllbnQ9PSJwNCJdDQphY3RpdmVfYW9pczwtIG5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpDQpgYGANCg0KIyA2LjUuMSBDbHVzdGVyaW5nIGhpZ2ggQ1YgZ2VuZXMgZm9yIHN1YnNldCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpDYWxjdWxhdGluZyBDViB2YWx1ZXMNCg0KYGBge3IgY2x1c3RlcmluZ19zdWJzZXQsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTE1fQ0KIyBjcmVhdGUgYSBsb2cyIHRyYW5zZm9ybSBvZiB0aGUgZGF0YSBmb3IgYW5hbHlzaXMNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtDQogIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCg0KIyBjcmVhdGUgQ1YgZnVuY3Rpb24NCmNhbGNfQ1YgPC0gZnVuY3Rpb24oeCkge3NkKHgpIC8gbWVhbih4KX0NCkNWX2RhdCA8LSBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YVssYWN0aXZlX2FvaXNdLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIsIE1BUkdJTiA9IDEsIGNhbGNfQ1YpDQoNCmBgYA0KDQojIyBUYWJsZSBvZiBDViB2YWx1ZXMNCg0KYGBge3J9DQojIHNob3cgdGhlIGhpZ2hlc3QgQ0QgZ2VuZXMgYW5kIHRoZWlyIENWIHZhbHVlcw0KZGF0YXRhYmxlKGFzLmRhdGEuZnJhbWUoQ1ZfZGF0KSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBvcmRlciA9IGxpc3QoMSwgJ2Rlc2MnKSwNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKSwgY2FwdGlvbiA9ICJDViB2YWx1ZXMgb2YgZ2VuZXMiIA0KKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJDVl9kYXQiKSwgZGlnaXRzPTMpDQoNCmBgYA0KDQojIyBIZWF0bWFwIG9uIG9mIHN1YnNldCwgZ2VuZXMgaW4gdGhlIHRvcCAzcmQgb2YgdGhlIENWIHZhbHVlcyB7LmFjdGl2ZX0NCg0KYGBge3IsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTE1fQ0KIyBJZGVudGlmeSBnZW5lcyBpbiB0aGUgdG9wIDNyZCBvZiB0aGUgQ1YgdmFsdWVzDQpHT0kgPC0gbmFtZXMoQ1ZfZGF0KVtDVl9kYXQgPiBxdWFudGlsZShDVl9kYXQsIDAuNzUpXQ0KcGhlYXRtYXAoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtHT0ksYWN0aXZlX2FvaXMgXSwgZWx0ID0gImxvZ19xIiksDQogICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgIGZvbnRzaXplX3JvdyA9IDUsDQogICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgIGFubm90YXRpb25fY29sID0NCiAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KYGBgDQoNCiMgNy4xIERpZmZlcmVudGlhbCBFeHByZXNzaW9uDQoNCiFbXShMOi9wa2xvb3N0ZXJtYW4vS2lkbmV5X29yZ2FuX2F0bGFzL2tpZG5leV9zdHJ1Y3R1cmUucG5nKQ0KDQojIyB0LXRlc3QNCg0KYGBge3IgdHRlc3R9DQojZ2MoKQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCWSINCiNvcHRpb25zOiAiaG9sbSIgICAgICAgImhvY2hiZXJnIiAgICJob21tZWwiICAgICAiYm9uZmVycm9uaSIgIkJIIiAgICAgICAgICJCWSIgICAgICAgICAiZmRyIiANCmNvdW50ZXI9MQ0KDQpjb21wc19kZjwtZGF0YS5mcmFtZShjb21wPScnLHZhbD0nJykNCg0KZm9yKHJlZ2lvbiBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjMpKSB7DQogIGZvciAoYWN0aXZlX2dyb3VwMSBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEpKSB7DQogICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSkpIHsNCiAgICAgIA0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhyZWdpb24sIGFjdGl2ZV9ncm91cDEsYWN0aXZlX2dyb3VwMikpLGNvbGxhcHNlID0gIl8iKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQogICAgICANCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FbLG5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpW3BEYXRhKHRhcmdldF9EYXRhKSRBTk4xPT1hY3RpdmVfZ3JvdXAxICYgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjM9PXJlZ2lvbl1dDQogICAgICBncm91cDI8LWxvZ19xWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMT09YWN0aXZlX2dyb3VwMiAmIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zPT1yZWdpb25dXQ0KICAgICAgDQogICAgICAjcnVuIHRfdGVzdHMgIA0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xLCAxLCBmdW5jdGlvbih4KSB0LnRlc3QoeFtjb2xuYW1lcyhncm91cDEpXSx4W2NvbG5hbWVzKGdyb3VwMildKSRwLnZhbHVlKSApDQogICAgICBjb2xuYW1lcyhyZXN1bHRzKTwtInJhd19wX3ZhbHVlIg0KICAgICAgDQogICAgICAjbXVsdGlwbGVfdGVzdGluZ19jb3JyZWN0aW9uDQogICAgICBhZGpfcF92YWx1ZTwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPW10YykNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsYWRqX3BfdmFsdWUpDQogICAgICANCiAgICAgICNjYWxjX2Zkcg0KICAgICAgRkRSPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9ImZkciIpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLEZEUikNCiAgICAgIA0KICAgICAgI2ZvbGRfY2hhbmdlcw0KICAgICAgI2FzIGJhc2UgZGF0YSBpcyBhbHJlYWR5IGxvZyB0cmFuc2Zvcm1lZCwgbWVhbnMgbmVlZCB0byBiZSBzdWJ0cmFjdGVkIHRvIGdldCBGQyBpbiBsb2cgc3BhY2UNCiAgICAgIGZjaGFuZ2VzPC1hcy5kYXRhLmZyYW1lKGFwcGx5KGxvZ19xLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkpKSkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgI3Bhc3RlKCJGQyIsYWN0aXZlX2dyb3VwMSwiIC8gIixhY3RpdmVfZ3JvdXAyKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCiAgICAgIA0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCiAgICAgIA0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDEiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkcmF3X3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwxXSA8LSAiTlMgb3IgRkMgPCAxIg0KICAgICAgcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IocmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQogICAgICANCiAgICAgICN2dWxjYW5vcGxvdA0KICAgICAgDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCiAgICAgIA0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1ssICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1ssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToyMF1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzWywgJ0dlbmUnXVtvcmRlcihyZXN1bHRzWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToyMF1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KICAgICAgDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIA0KICAgICAgcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEZDLCB5ID0gLWxvZzEwKHJhd19wX3ZhbHVlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gR2VuZSkpICsNCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygxLCAtMSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgICAgbGFicyh4ID0gcGFzdGUoIkVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCAoLTE+RkN8IEZDPjEpICYgRkRSIDwgMC4wNSAmIEdlbmUgJWluJSB0b3BfZyksDQogICAgICAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLCBzaXplPTMuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICAgICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTApICsNCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgICAgZ2d0aXRsZShwYXN0ZSgiY2xhc3M6ICIsIHJlZ2lvbiwiIC0gIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KICAgICAgDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KICAgICAgDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCn0NCmdyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KYGBge3IgZHVtcF90YWJsZXMscmVzdWx0cz0nYXNpcyd9DQojc3RyYW5nbHkgZG9lcyBub3QgYXBwZWFyIGluIGh0bWwgb3V0cHV0IC0+IGNodWNrcyBhcmUgbGltaXRlZCB0byBvbmUgZGF0YXRhYmxlIHBlciBjaHVjay4gVGhlIGh0bWx0b29scyBhcmUgYSB3YXkgYXJvdW5kIHRoaXMgYnV0IGRvZXMgc2hvdyB0aGVtIHN0YWNrZWQgaW4gYSBib3guDQpyZXN1bHRzX3RhYmxlcyA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQ0KZm9yIChjIGluICgyOmNvdW50ZXItMSkpIHsNCiAgI0dlbmUgJWluJSBHT0kNCiAgcmVzdWx0c190YWJsZXNbW2NdXSA8LSBkYXRhdGFibGUoc3Vic2V0KHRhYmxlc1tbY11dLCBDb2xvciA9PSBjKCJGRFIgPCAwLjAwMSIsICAiUCA8IDAuMDUiKSksIA0KICAgICAgICAgICByb3duYW1lcz1GQUxTRSwNCiAgICAgICAgICAgZXh0ZW5zaW9ucyA9IGMoJ0J1dHRvbnMnKSwgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgICApLCBoZWlnaHQgPSA1NTAsDQogICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBsYWJlbHNbW2NdXSksZmlsdGVyPSd0b3AnKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJyYXdfcF92YWx1ZSIsImFkal9wX3ZhbHVlIiwiRkRSIiwiRkMiKSwgZGlnaXRzPTMpDQogICNjYXQoJ1xuXG48IS0tIC0tPlxuXG4nKQ0KfSAgICAgICAgICAgIA0KcmVzdWx0c190YWJsZXMNCmBgYA0KDQpgYGB7cn0NCnByaW50KCIiKSAjIGZvciBsYXlvdXQgb2YgbmV4dCBzZWN0aW9uDQpgYGANCg0KIyA3LjIgREUgYW5hbHlzaXMgd2l0aCBMTU0NCg0KQSBjb21tb24gc3RhdGlzdGljYWwgYXBwcm9hY2ggaXMgdG8gdXNlIGEgbGluZWFyIG1peGVkLWVmZmVjdCBtb2RlbA0KKExNTSkuIFRoZSBMTU0gYWxsb3dzIHRoZSB1c2VyIHRvIGFjY291bnQgZm9yIHRoZSBzdWJzYW1wbGluZyBwZXINCnRpc3N1ZTsgaW4gb3RoZXIgd29yZHMsIHdlIGFkanVzdCBmb3IgdGhlIGZhY3QgdGhhdCB0aGUgbXVsdGlwbGUgcmVnaW9ucw0Kb2YgaW50ZXJlc3QgcGxhY2VkIHBlciB0aXNzdWUgc2VjdGlvbiBhcmUgbm90IGluZGVwZW5kZW50IG9ic2VydmF0aW9ucywNCmFzIGlzIHRoZSBhc3N1bXB0aW9uIHdpdGggb3RoZXIgdHJhZGl0aW9uYWwgc3RhdGlzdGljYWwgdGVzdHMuIFRoZQ0KZm9ybXVsYXRpb24gb2YgdGhlIExNTSBtb2RlbCBkZXBlbmRzIG9uIHRoZSBzY2llbnRpZmljIHF1ZXN0aW9uIGJlaW5nDQphc2tlZC4NCg0KT3ZlcmFsbCwgdGhlcmUgYXJlIHR3byBmbGF2b3JzIG9mIHRoZSBMTU0gbW9kZWwgd2hlbiB1c2VkIHdpdGggR2VvTXgNCmRhdGE6IGkpIHdpdGggYW5kIGlpKSB3aXRob3V0IHJhbmRvbSBzbG9wZS4NCg0KV2hlbiBjb21wYXJpbmcgZmVhdHVyZXMgdGhhdCBjby1leGlzdCBpbiBhIGdpdmVuIHRpc3N1ZSBzZWN0aW9uLCBhDQpyYW5kb20gc2xvcGUgaXMgaW5jbHVkZWQgaW4gdGhlIExNTSBtb2RlbC4gV2hlbiBjb21wYXJpbmcgZmVhdHVyZXMgdGhhdA0KYXJlIG11dHVhbGx5IGV4Y2x1c2l2ZSBpbiBhIGdpdmVuIHRpc3N1ZSBzZWN0aW9uIHRoZSBMTU0gbW9kZWwgZG9lcyBub3QNCnJlcXVpcmUgYSByYW5kb20gc2xvcGUuDQoNCk1vc3RseSwgd2UgdXNlIHBhdGllbnQvc2FtcGxlIGFzIFJhbmRvbSBJbnRlcmNlcHQgd2hlbiB0aGV5IGFyZSBjb21iaW5lZA0Kb24gc2xpZGVzLg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvTE1NX3NldHVwLnBuZykNCg0KYGBge3IgTE1NIHJlZ2lvbn0NCiMgY29udmVydCB0ZXN0IHZhcmlhYmxlcyB0byBmYWN0b3JzDQpwRGF0YSh0YXJnZXRfRGF0YSkkdGVzdENsYXNzIDwtIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSwgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjEpKQ0KcERhdGEodGFyZ2V0X0RhdGEpW1sic2xpZGUiXV0gPC0gZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKVtbInNsaWRlX25hbWUiXV0pDQojYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsIDIsIEZVTiA9IGxvZywgYmFzZSA9IDIsIGVsdCA9ICJxX25vcm0iKQ0KDQojIHJ1biBMTU06DQojIGZvcm11bGEgZm9sbG93cyBjb252ZW50aW9ucyBkZWZpbmVkIGJ5IHRoZSBsbWU0IHBhY2thZ2UNCmxtbV9yZXN1bHRzIDwtIGMoKQ0KZm9yIChyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICAgIGluZCA8LSBwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMyA9PSByZWdpb24NCg0KICAgIG1peGVkT3V0bWMgPC0NCiAgICAgICAgbWl4ZWRNb2RlbERFKHRhcmdldF9EYXRhWyxpbmRdLA0KICAgICAgICAgICAgICAgICAgICAgZWx0ID0gImxvZ19xIiwNCiAgICAgICAgICAgICAgICAgICAgIG1vZGVsRm9ybXVsYSA9IH4gdGVzdENsYXNzICsgKDEgfCBzbGlkZSksDQogICAgICAgICAgICAgICAgICAgICBncm91cFZhciA9ICJ0ZXN0Q2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICAgbkNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCksDQogICAgICAgICAgICAgICAgICAgICBtdWx0aUNvcmUgPSBUUlVFKQ0KDQogICAgIyBmb3JtYXQgcmVzdWx0cyBhcyBkYXRhLmZyYW1lDQogICAgcl90ZXN0IDwtIGRvLmNhbGwocmJpbmQsIG1peGVkT3V0bWNbImxzbWVhbnMiLCBdKQ0KICAgIHRlc3RzIDwtIHJvd25hbWVzKHJfdGVzdCkNCiAgICByX3Rlc3QgPC0gYXMuZGF0YS5mcmFtZShyX3Rlc3QpDQogICAgcl90ZXN0JENvbnRyYXN0IDwtIHRlc3RzDQoNCiAgICAjIHVzZSBsYXBwbHkgaW4gY2FzZSB5b3UgaGF2ZSBtdWx0aXBsZSBsZXZlbHMgb2YgeW91ciB0ZXN0IGZhY3RvciB0bw0KICAgICMgY29ycmVjdGx5IGFzc29jaWF0ZSBnZW5lIG5hbWUgd2l0aCBpdCdzIHJvdyBpbiB0aGUgcmVzdWx0cyB0YWJsZQ0KICAgIHJfdGVzdCRHZW5lIDwtDQogICAgICAgIHVubGlzdChsYXBwbHkoY29sbmFtZXMobWl4ZWRPdXRtYyksDQogICAgICAgICAgICAgICAgICAgICAgcmVwLCBucm93KG1peGVkT3V0bWNbImxzbWVhbnMiLCBdW1sxXV0pKSkNCiAgICByX3Rlc3QkU3Vic2V0IDwtIHJlZ2lvbg0KICAgIHJfdGVzdCRGRFIgPC0gcC5hZGp1c3Qocl90ZXN0JGBQcig+fHR8KWAsIG1ldGhvZCA9ICJmZHIiKQ0KICAgIHJfdGVzdCA8LSByX3Rlc3RbLCBjKCJHZW5lIiwgIlN1YnNldCIsICJDb250cmFzdCIsICJFc3RpbWF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlByKD58dHwpIiwgIkZEUiIpXQ0KICAgIA0KICAgIGxtbV9yZXN1bHRzIDwtIHJiaW5kKGxtbV9yZXN1bHRzLCByX3Rlc3QpDQp9DQpgYGANCg0KIyA3LjMgVnVsY2Fub3Bsb3QgKyB0YWJsZSBvZiBMTU0NCg0KYGBge3IgbG1tX3Z1bGNhbm8sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTEwfQ0KIyBDYXRlZ29yaXplIFJlc3VsdHMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCmZjX3RocmVzaG9sZCA9IDENCg0KbG1tX3Jlc3VsdHMkQ29sb3IgPC0gcGFzdGUoIk5TIG9yIEZDIDwgIixmY190aHJlc2hvbGQgPSAxKQ0KbG1tX3Jlc3VsdHMkQ29sb3JbbG1tX3Jlc3VsdHMkYFByKD58dHwpYCA8IDAuMDVdIDwtICJQIDwgMC4wNSINCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KbG1tX3Jlc3VsdHMkQ29sb3JbbG1tX3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCmxtbV9yZXN1bHRzJENvbG9yW2FicyhsbW1fcmVzdWx0cyRFc3RpbWF0ZSkgPCBmY190aHJlc2hvbGRdIDwtIHBhc3RlKCJOUyBvciBGQyA8ICIsZmNfdGhyZXNob2xkID0gMSkNCmxtbV9yZXN1bHRzJENvbG9yIDwtIGZhY3RvcihsbW1fcmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMSIsICJQIDwgMC4wNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQpjb3VudGVyID0gMQ0KcGxvdHNfbG1tIDwtIGxpc3QoKQ0KZm9yKGMgaW4gdW5pcXVlKGxtbV9yZXN1bHRzJFN1YnNldCkgKSB7DQogIGxtbV9yZXN1bHRzX3NsaWNlID0gbG1tX3Jlc3VsdHNbbG1tX3Jlc3VsdHMkU3Vic2V0ID09IGMsXQ0KDQogICMgcGljayB0b3AgZ2VuZXMgZm9yIGVpdGhlciBzaWRlIG9mIHZvbGNhbm8gdG8gbGFiZWwNCiAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQogIGxtbV9yZXN1bHRzX3NsaWNlJGludmVydF9QIDwtICgtbG9nMTAobG1tX3Jlc3VsdHNfc2xpY2UkYFByKD58dHwpYCkpICogc2lnbihsbW1fcmVzdWx0c19zbGljZSRFc3RpbWF0ZSkNCiAgDQogICNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQogIHRvcF9nIDwtIGMoKQ0KICB0b3BfZyA8LSBjKHRvcF9nLA0KICAgIGxtbV9yZXN1bHRzX3NsaWNlWywgJ0dlbmUnXVsNCiAgICAgIG9yZGVyKGxtbV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgbG1tX3Jlc3VsdHNfc2xpY2VbLCAnR2VuZSddWw0KICAgICAgb3JkZXIobG1tX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQoNCiAgdG9wX2cgPC0gdW5pcXVlKHRvcF9nKQ0KICANCiAgbG1tX3Jlc3VsdHNfc2xpY2UgPC0gbG1tX3Jlc3VsdHNfc2xpY2VbLCAtMSpuY29sKGxtbV9yZXN1bHRzX3NsaWNlKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgDQogICMgRmxpcCBDb250cmFzdA0KICBjb250cmFzdF9sYWIgPC0gYXMuY2hhcmFjdGVyKGxtbV9yZXN1bHRzX3NsaWNlJENvbnRyYXN0KQ0KICBjb250cmFzdF9sYWIgPC0gc3Ryc3BsaXQoY29udHJhc3RfbGFiLCAiLSIpW1sxXV0NCiAgY29udHJhc3RfbGFiIDwtIHBhc3RlKGNvbnRyYXN0X2xhYlsyXSwgIi0iLCBjb250cmFzdF9sYWJbMV0pDQogIA0KICAjIEdyYXBoIHJlc3VsdHMNCiAgcGxvdHNfbG1tW1tjb3VudGVyXV0gPC0gZ2dwbG90KGxtbV9yZXN1bHRzX3NsaWNlLA0KICAgICAgICAgICAgICAgYWVzKHggPSBFc3RpbWF0ZSwgeSA9IC1sb2cxMChgUHIoPnx0fClgKSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhmY190aHJlc2hvbGQsIC1mY190aHJlc2hvbGQpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICB4ID0gcGFzdGUoY29udHJhc3RfbGFiLCAiIGxvZzIoRkMpIiksDQogICAgICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQobG1tX3Jlc3VsdHNfc2xpY2UsICBgUHIoPnx0fClgIDwgMC4wMDEgJiBhYnMobG1tX3Jlc3VsdHNfc2xpY2UkRXN0aW1hdGUpID4gZmNfdGhyZXNob2xkICYgR2VuZSAlaW4lIHRvcF9nKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgICAgICBmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KICBjb3VudGVyIDwtIGNvdW50ZXIgKyAxDQp9DQpncmlkLmFycmFuZ2UoZ3JvYnM9cGxvdHNfbG1tLG5jb2w9MikNCmBgYCAgICANCg0KYGBge3IgdGFibGVfb2ZfTE1NX3Jlc3VsdHMgcmVnaW9ufQ0KI3N1YnNldChsbW1fcmVzdWx0cywgR2VuZSAlaW4lIEdPSSkNCmRhdGF0YWJsZShsbW1fcmVzdWx0cywgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBHZW5lcyBvZiBJbnRlcmVzdCAoPjc1JSBDVikiLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiRXN0aW1hdGUiLCJQcig+fHR8KSIsIkZEUiIpLCBkaWdpdHM9MykNCmBgYA0KDQojIDcuNCBQbG90dGluZyBHZW5lcyBvZiBJbnRlcmVzdA0KDQpgYGB7ciBwbG90X2dlbmVfb2ZfaW50ZXJlc3QsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTV9DQpteV9nb2lzIDwtdW5pcXVlKHN1YnNldChsbW1fcmVzdWx0cywgYEZEUmAgPCAwLjAwMSkkR2VuZSkNCnRtcF90Ymw8LXN1YnNldChsbW1fcmVzdWx0cywgR2VuZSAlaW4lIG15X2dvaXMpDQoNCm15X2dvaXMgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZSh0bXBfdGJsJENvbnRyYXN0KSkgeyAjIGdlbmUgb2YgaW50ZXJlc3QgZm9yIGFsbCBjb250cmFzdHMNCiNmb3IgKGNvbnRyYXN0IGluIGMoIkp1eF9HbG8gLSBQcm9fVHViIikpIHsNCiAgaW5kIDwtIHRtcF90YmwkQ29udHJhc3QgPT0gY29udHJhc3QNCiAgZ29pIDwtIHRtcF90YmxbaW5kLCAiR2VuZSJdW29yZGVyKHRtcF90YmxbaW5kLCAiRXN0aW1hdGUiXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjNdXQ0KICBteV9nb2lzIDwtIGMobXlfZ29pcywgZ29pKQ0KICBnb2kgPC0gdG1wX3RibFtpbmQsICJHZW5lIl1bb3JkZXIodG1wX3RibFtpbmQsICJFc3RpbWF0ZSJdLCBkZWNyZWFzaW5nID0gVFJVRSlbMTozXV0NCiAgbXlfZ29pcyA8LSBjKG15X2dvaXMsIGdvaSkNCn0NCg0KaWYobnJvdyh0bXBfdGJsKSA+IDEpIHsgDQogIGRhdGF0YWJsZSh0bXBfdGJsLHJvd25hbWVzID0gRkFMU0UsY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBHZW5lcyBvZiBJbnRlcmVzdCAiLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiRXN0aW1hdGUiLCJQcig+fHR8KSIsIkZEUiIpLCBkaWdpdHM9MykNCiANCmZvciAobXlfZ29pIGluIG15X2dvaXMpIHsNCiMgc2hvdyBleHByZXNzaW9uIGZvciBhIHNpbmdsZSB0YXJnZXQNCiAgYTwtZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IEFOTjIsIGZpbGwgPSBBTk4yLA0KICAgICAgICAgICB5ID0gYXMubnVtZXJpYyhhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhW215X2dvaSwgXSwgZWx0ID0gInFfbm9ybSIpKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gcGFzdGUobXlfZ29pLCJFeHByZXNzaW9uIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogICNmYWNldF93cmFwKH5BTk4zLCBucm93PTEpICsgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsNCiAgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKQ0KICBwcmludChhKQ0KfQ0KfWVsc2V7DQogIHByaW50KCJObyBzaWduaWZpY2FudCBsTU0gcmVzdWx0cyB0byBwbG90IikNCn0NCmBgYA0KDQojIDcuNSBIZWF0bWFwIG9mIFNpZ25pZmljYW50IEdlbmVzDQoNCkluIGFkZGl0aW9uIHRvIGdlbmVyYXRpbmcgaW5kaXZpZHVhbCBnZW5lIGJveCBwbG90cyBvciB2b2xjYW5vIHBsb3RzLCB3ZQ0KY2FuIGFnYWluIGNyZWF0ZSBhIGhlYXRtYXAgZnJvbSBvdXIgZGF0YS4gVGhpcyB0aW1lIHJhdGhlciB0aGFuDQp1dGlsaXppbmcgQ1YgdG8gc2VsZWN0IGdlbmVzLCB3ZSBjYW4gdXNlIHRoZSBQLXZhbHVlIG9yIEZEUiB2YWx1ZXMgdG8NCnNlbGVjdCBnZW5lcy4gSGVyZSwgd2UgcGxvdCBhbGwgZ2VuZXMgd2l0aCBhbiBGRFIgXDwgMC4wMDEuDQoNCmBgYHtyIGhlYXRtYXBfc2lnX2dlbmVzLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCm15X2dvaXMgPC11bmlxdWUoc3Vic2V0KGxtbV9yZXN1bHRzLCBgRkRSYCA8IDAuMDAxKSRHZW5lKSAgICMgMTAwIHRvIHByZXZlbnQgbG9uZyBydW50aW1lDQoNCmlmKGxlbmd0aChteV9nb2lzKTwyKSB7DQogIHByaW50KCJObyBzaWduaWZpY2FudCByZXN1bHRzIHRvIHNob3ciKQ0KIA0KfWVsc2V7DQoNCnBoZWF0bWFwKGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtteV9nb2lzLCBdLCBlbHQgPSAicV9ub3JtIikpLA0KICAgICAgICAgc2NhbGUgPSAicm93IiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgYnJlYWtzID0gc2VxKC0zLCAzLCAwLjA1KSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCn0NCmBgYA0KDQojIDggUGF0aHdheSBBbmFseXNpcw0KDQpQYXRod2F5IGFuYWx5c2lzIGVuYWJsZXMgZXhwbG9yYXRpb24gb2YgZGlmZmVyZW50IGFnZ3JlZ2F0ZSBnZW5lIHNldHMNCmZvciBvdXIgZXhwZXJpbWVudGFsIHF1ZXN0aW9ucy4gRWFjaCBpbmRpdmlkdWFsIFJPSS9BT0kgc2VnbWVudCBpcw0Kc2NvcmVkIGZvciBldmVyeSBwYXRod2F5IG9mIGludGVyZXN0LCB3aGljaCB3ZSBjYW4gdGhlbiB1c2UgdG8NCmludmVzdGlnYXRlIGJpb2xvZ2ljYWwgZGlmZmVyZW5jZXMuIFdlIHdpbGwgcGVyZm9ybSBhbmFsb2dvdXMgYW5hbHlzZXMNCmFzIHRob3NlIG91dGxpbmVkIGluIHRoZSBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBhbmQgU3BhdGlhbA0KRGVjb252b2x1dGlvbiBzZWN0aW9ucyBvZiB0aGUgcmVwb3J0IGZvciBnZW5lIHNldCBlbnJpY2htZW50Lg0KDQojIDguMSBTY29yaW5nIEdlbmUgU2V0cw0KDQpQYXRod2F5cyBhbmQgZ2VuZSBzZXRzIHdlcmUgZGVmaW5lZCBmcm9tIHRoZSBLZWdnIEJyaXRlIGRhdGFiYXNlLiBXZSB1c2UNCmFuIFIgc29mdHdhcmUgcGFja2FnZSBjYWxsZWQgR2VuZSBTZXQgVmFyaWF0aW9uIEFuYWx5c2lzIHRvIHNjb3JlIGVhY2gNCnNlZ21lbnQgd2l0aGluIG91ciBzdHVkeS4gc2VlDQo8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21zaWdkYnIvdmlnbmV0dGVzL21zaWdkYnItaW50cm8uaHRtbD4NCmZvciBvcHRpb25zIG9uIGNvbGxlY3Rpb25zLiBXZSB1c2UgdGhlIEtFR0cgYW5kIFJFQUNUT01FLg0KDQpgYGB7ciBidWlsZF9nZW5lc2V0c30NCiNnYygpDQpoX2dlbmVfc2V0cyA9IHJiaW5kKG1zaWdkYnIoc3BlY2llcyA9ICJodW1hbiIsIHN1YmNhdGVnb3J5ID0gIkNQOktFR0ciKSwNCiAgICAgICAgICAgICAgICAgICAgbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgc3ViY2F0ZWdvcnkgPSAiQ1A6UkVBQ1RPTUUiKSkNCiNtc2lnZGJyKHNwZWNpZXMgPSAiaHVtYW4iLCBzdWJjYXRlZ29yeSA9ICJDUDpCSU9DQVJUQSIpDQoNCm1zaWdkYnJfbGlzdCA9IHNwbGl0KHggPSBoX2dlbmVfc2V0cyRnZW5lX3N5bWJvbCwgZiA9IGhfZ2VuZV9zZXRzJGdzX25hbWUpDQoNCiMgcHJlcGFyZSBkZiBmb3IgYWNjdXJhdGUgbWVyZ2luZyBiYWNrIGdlbmVzIGxhdGVyDQpwd19nZW5lX2RmPC1kYXRhLmZyYW1lKFBhdGh3YXkgPSBuYW1lcyhtc2lnZGJyX2xpc3QpKQ0KcHdfZ2VuZV9kZiRnZW5lczwtbXNpZ2Ricl9saXN0DQpgYGANCg0KYGBge3IgcnVuX2dzdmF9DQpzc2dzZWFfcmVzdWx0cyA8LSBHU1ZBOjpnc3ZhKGV4cHIgPSBhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzZXQuaWR4Lmxpc3QgPSBtc2lnZGJyX2xpc3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInNzZ3NlYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnN6ID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguc3ogPSA1MDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQ0KZ2VuZVNldE9iaiA8LQ0KICBOYW5vU3RyaW5nR2VvTXhTZXQoYXNzYXlEYXRhID0gc3Nnc2VhX3Jlc3VsdHMsDQogICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdGF0ZWREYXRhRnJhbWUocERhdGEodGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YSA9IHByb3RvY29sRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlVHlwZSA9ICJHZW5lU2V0IiwNCiAgICAgICAgICAgICAgICAgICAgIGNoZWNrID0gRkFMU0UpDQpgYGANCg0KIyA4LjIgRGlmZmVyZW50YWwgYW5hbHlzaXMgb2YgcGF0aHdheXMNCg0KYGBge3IgTE1NX29mX3BhdGh3YXlfYW5hbGlzaXMgcmVnaW9ufQ0KIyAjIGNvbnZlcnQgdGVzdCB2YXJpYWJsZXMgdG8gZmFjdG9ycw0KcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RDbGFzcyA8LSBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4xKSkNCnBEYXRhKGdlbmVTZXRPYmopW1sic2xpZGUiXV08LWZhY3RvcihwRGF0YShnZW5lU2V0T2JqKVtbInNsaWRlX25hbWUiXV0pDQojcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RSZWdpb248LWZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMiwgdW5pcXVlKGNvdW50X21hdCRBTk4zKSkNCg0KbG1tX3NzZ3NlYV9yZXN1bHRzIDwtIGMoKQ0KZm9yIChyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICBpbmQgPC0gcERhdGEodGFyZ2V0X0RhdGEpJEFOTjMgPT0gcmVnaW9uDQogIG1peGVkT3V0bWMgPC0NCiAgICBtaXhlZE1vZGVsREUoZ2VuZVNldE9ialssIGluZF0sDQogICAgICAgICAgICAgICAgIGVsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgIG1vZGVsRm9ybXVsYSA9IH4gdGVzdENsYXNzICsgKDEgfCBzbGlkZSksDQogICAgICAgICAgICAgICAgIGdyb3VwVmFyID0gInRlc3RDbGFzcyIsDQogICAgICAgICAgICAgICAgIG5Db3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLA0KICAgICAgICAgICAgICAgICBtdWx0aUNvcmUgPSBUUlVFKQ0KDQogICMgZm9ybWF0IHJlc3VsdHMgYXMgZGF0YS5mcmFtZQ0KICByX3NzZ3NlYV90ZXN0IDwtIGRvLmNhbGwocmJpbmQsIG1peGVkT3V0bWNbImxzbWVhbnMiLCBdKQ0KICBzc2dzZWFfdGVzdHMgPC0gcm93bmFtZXMocl9zc2dzZWFfdGVzdCkNCiAgcl9zc2dzZWFfdGVzdCA8LSBhcy5kYXRhLmZyYW1lKHJfc3Nnc2VhX3Rlc3QpDQogIHJfc3Nnc2VhX3Rlc3QkQ29udHJhc3QgPC0gc3Nnc2VhX3Rlc3RzDQogICNyX3NzZ3NlYV90ZXN0JEdlbmVzIDwtIG1zaWdkYnJfbGlzdCBzZWVtcyB1bnJlbGlhYmxlIGFzIGdzdmEgb21pdHMgcGF0aHdheXMgaWYgZ2VuZXMgYXJlIG5vdCBpbiBkYXRhLi4NCg0KICAjIHVzZSBsYXBwbHkgaW4gY2FzZSB5b3UgaGF2ZSBtdWx0aXBsZSBsZXZlbHMgb2YgeW91ciB0ZXN0IGZhY3RvciB0bw0KICAjIGNvcnJlY3RseSBhc3NvY2lhdGUgZ2VuZSBuYW1lIHdpdGggaXQncyByb3cgaW4gdGhlIHJlc3VsdHMgdGFibGUNCiAgcl9zc2dzZWFfdGVzdCRQYXRod2F5IDwtDQogICAgdW5saXN0KGxhcHBseShjb2xuYW1lcyhtaXhlZE91dG1jKSwNCiAgICAgICAgICAgICAgICAgIHJlcCwgbnJvdyhtaXhlZE91dG1jWyJsc21lYW5zIiwgXVtbMV1dKSkpDQoNCiAgI3Jfc3Nnc2VhX3Rlc3QkU3Vic2V0IDwtIHN0YXR1cw0KICByX3NzZ3NlYV90ZXN0JEZEUiA8LSBwLmFkanVzdChyX3NzZ3NlYV90ZXN0JGBQcig+fHR8KWAsIG1ldGhvZCA9ICJmZHIiKQ0KICByX3NzZ3NlYV90ZXN0JFN1YnNldCA8LSByZWdpb24NCiAgcl9zc2dzZWFfdGVzdCA8LSByX3NzZ3NlYV90ZXN0WywgYygiUGF0aHdheSIsICJTdWJzZXQiLCAiQ29udHJhc3QiLCAiRXN0aW1hdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcig+fHR8KSIsICJGRFIiKV0NCiAgbG1tX3NzZ3NlYV9yZXN1bHRzIDwtIHJiaW5kKGxtbV9zc2dzZWFfcmVzdWx0cywgcl9zc2dzZWFfdGVzdCkNCg0KICB9DQogIGxtbV9zc2dzZWFfcmVzdWx0cyA8LSBtZXJnZShsbW1fc3Nnc2VhX3Jlc3VsdHMsIHB3X2dlbmVfZGYpDQpgYGANCg0KIyA4LjIuMSBUYWJsZSBvZiBEaWZmZXJlbnRhbCBhbmFseXNpcyBvZiBwYXRod2F5cw0KDQpgYGB7ciB0YWJsZV9vZl9MTU1fcGF0aHdheV9yZXN1bHRzIHJlZ2lvbiwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQpkYXRhdGFibGUoc3Vic2V0KGxtbV9zc2dzZWFfcmVzdWx0cyksIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwNCiAgICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICAgKSwNCiAgICAgICAgICBjYXB0aW9uID0gIkRFIHJlc3VsdHMgZm9yIFBhdGh3YXlzIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTUpDQpgYGANCg0KIyA4LjMgVnVsY2Fub3Bsb3Qgb2YgTE1NX1BhdGh3YXlzDQoNCmBgYHtyIGxtbV9wd192dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAwLjMNCg0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuMyINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvcltsbW1fc3Nnc2VhX3Jlc3VsdHMkYFByKD58dHwpYCA8IDAuMDVdIDwtICJQIDwgMC4wNSINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvcltsbW1fc3Nnc2VhX3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbYWJzKGxtbV9zc2dzZWFfcmVzdWx0cyRFc3RpbWF0ZSkgPCBmY190aHJlc2hvbGRdIDwtICJOUyBvciBGQyA8IDAuMyINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IobG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjMiLCAiUCA8IDAuMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQojIHBpY2sgdG9wIHB3IGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQojIG9yZGVyIHB3IGZvciBjb252ZW5pZW5jZToNCmxtbV9zc2dzZWFfcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKGxtbV9zc2dzZWFfcmVzdWx0cyRgUHIoPnx0fClgKSkgKiBzaWduKGxtbV9zc2dzZWFfcmVzdWx0cyRFc3RpbWF0ZSkNCnRvcF9zc2dzZWFfZyA8LSBjKCkNCg0KI2xvb3AgaGVyZSBvdmVyIHRlc3RlZCBjb25kaXRpb25zIGlmIGFwcGxpY2FibGUNCg0KZm9yKGMgaW4gdW5pcXVlKGxtbV9zc2dzZWFfcmVzdWx0cyRTdWJzZXQpICkgew0KICBsbW1fcmVzdWx0c19zbGljZSA9IGxtbV9zc2dzZWFfcmVzdWx0c1tsbW1fc3Nnc2VhX3Jlc3VsdHMkU3Vic2V0ID09IGMsXQ0KIHRvcF9zc2dzZWFfZyA8LSBjKHRvcF9zc2dzZWFfZywNCiAgICAgICAgIGxtbV9zc2dzZWFfcmVzdWx0c1tpbmQsICdQYXRod2F5J11bDQogICAgICAgICAgICAgICBvcmRlcihsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgIGxtbV9zc2dzZWFfcmVzdWx0c1tpbmQsICdQYXRod2F5J11bDQogICAgICAgICAgICAgICBvcmRlcihsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQp9DQogDQp0b3Bfc3Nnc2VhX2cgPC0gdW5pcXVlKHRvcF9zc2dzZWFfZykNCmxtbV9zc2dzZWFfcmVzdWx0cyA8LSBsbW1fc3Nnc2VhX3Jlc3VsdHNbLCAtMSpuY29sKGxtbV9zc2dzZWFfcmVzdWx0cyldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQoNCiNsbW1fc3Nnc2VhX3Jlc3VsdHNfc2xpY2UgPC0gbG1tX3NzZ3NlYV9yZXN1bHRzX3NsaWNlW2xtbV9zc2dzZWFfcmVzdWx0c19zbGljZSRGRFIgPCAxLF0NCg0KIyBHcmFwaCByZXN1bHRzDQpnZ3Bsb3QobG1tX3NzZ3NlYV9yZXN1bHRzLA0KICAgICAgIGFlcyh4ID0gRXN0aW1hdGUsIHkgPSAtbG9nMTAoYFByKD58dHwpYCksDQogICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gUGF0aHdheSkpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDAuMiwgLTAuMiksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgbGFicyh4ID0gcGFzdGUobG1tX3Jlc3VsdHNfc2xpY2UkQ29udHJhc3QsICJsb2cyKEZDKSIpLA0KICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuNWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQobG1tX3NzZ3NlYV9yZXN1bHRzLCBDb2xvciA9PSAiRkRSIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSwNCiAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLHNpemU9MywNCiAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICBmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KDQojICAgICtmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKSkNCmBgYA0KDQojIDguNCBoZWF0bWFwIG9mIHBhdGh3YXlzDQoNCmBgYHtyIHB3X2hlYXRtYXAsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQ0KICBhY3RpdmVfcHc8LWZpbHRlcihsbW1fc3Nnc2VhX3Jlc3VsdHMsIGBQcig+fHR8KWAgPCAwLjAwMSkkUGF0aHdheQ0KICBhY3RpdmVfcHc8LWZpbHRlcihsbW1fc3Nnc2VhX3Jlc3VsdHMsIEZEUiA8IDAuMDAxICkkUGF0aHdheQ0KICAjYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSRQYXRod2F5DQogIA0KICANCiAgYWN0aXZlX3B3PC10b3Bfc3Nnc2VhX2cNCiAgDQogIGlmIChsZW5ndGgoYWN0aXZlX3B3KT4xKSB7DQogIA0KICAgIHByaW50KCJnbyIpDQogICAgDQogIHB3X21hdHJpeDwtYXNzYXlEYXRhRWxlbWVudChnZW5lU2V0T2JqLCBlbHQgPSAiZXhwcnMiKQ0KDQogIHBoZWF0bWFwKHB3X21hdHJpeFthY3RpdmVfcHcsXSwNCiAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMCwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJldWNsaWRlYW4iLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAyLCBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICAgI2NvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICAgIG1haW4gPSAiSGVhdG1hcCBvZiBzZWxlY3RlZCBQYXRod2F5cyIsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQogIA0KICB9ZWxzZXsNCiAgICBwcmludCgiTm8gc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBkaXNwbGF5IikNCiAgfQ0KYGBgDQoNCiMgOC41IGZnc2VhIHBhdGh3YXkgYW5hbHlzaXMNCg0KQW5vdGhlciBvcHRpb24gZm9yIHBhdGh3YXkgYW5hbHlzaXMgaXMgdGhlIFIgc29mdHdhcmUgcGFja2FnZSBjYWxsZWQgRmFzdCBHZW5lIFNldCBFbnJpY2htZW50IEFuYWx5c2lzLiBJdCBpcyBhIHByZS1yYW5rZWQgbWV0aG9kIHRoYXQgdXNlcyB0aGUgTE1NIHJlc3VsdHMgY29tYmluZWQgd2l0aCB0aGUgc2FtZSBwYXRod2F5IGxpc3QgZnJvbSBtc2lnZGJyIGFzIEdTVkEuDQoNCmBgYHtyIGZnc2VhfQ0KZmdzZWFfcmVzdWx0c19hbGwgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZShsbW1fcmVzdWx0cyRDb250cmFzdCkpIHsNCiAgICANCiAgICByYW5rcyA8LSBsbW1fcmVzdWx0cyRFc3RpbWF0ZVtsbW1fcmVzdWx0cyRDb250cmFzdCA9PSBjb250cmFzdF0NCiAgICBuYW1lcyhyYW5rcykgPC0gbG1tX3Jlc3VsdHMkR2VuZVtsbW1fcmVzdWx0cyRDb250cmFzdCA9PSBjb250cmFzdF0NCiAgICANCiAgICBmZ3NlYV9yZXN1bHRzIDwtIGZnc2VhKG1zaWdkYnJfbGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcmFua3MsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVwcyA9IDAuMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaW5TaXplPTE1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICBtYXhTaXplID0gNTAwKQ0KICAgIGZnc2VhX3Jlc3VsdHMkQ29udHJhc3QgPC0gY29udHJhc3QNCiAgICBmZ3NlYV9yZXN1bHRzX2FsbCA8LSByYmluZChmZ3NlYV9yZXN1bHRzX2FsbCwgZmdzZWFfcmVzdWx0cykNCiAgICANCn0NCg0KZmdzZWFfcmVhY3RvbWUgPC0gZmdzZWFfcmVzdWx0c19hbGxbZ3JlcCgiUkVBQ1RPTUUiLCBwYXRod2F5KSxdDQpmZ3NlYV9yZWFjdG9tZSRwYXRod2F5IDwtIGdzdWIoIlJFQUNUT01FXyIsICIiLCBmZ3NlYV9yZWFjdG9tZSRwYXRod2F5KQ0KZmdzZWFfcmVhY3RvbWUkcGF0aHdheSA8LSBnc3ViKCJfIiwgIiAiLCBmZ3NlYV9yZWFjdG9tZSRwYXRod2F5KQ0KYGBgDQoNCmBgYHtyIGZnc2VhX3B3X3Z1bGNhbm8sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTEwfQ0KIyBDYXRlZ29yaXplIFJlc3VsdHMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCmZjX3RocmVzaG9sZCA9IDAuMw0KDQoNCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yIDwtICJOUyBvciBGQyA8IDAuMyINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yW2Znc2VhX3Jlc3VsdHNfYWxsJHB2YWwgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwYWRqIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwYWRqIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yW2FicyhmZ3NlYV9yZXN1bHRzX2FsbCRFUykgPCBmY190aHJlc2hvbGRdIDwtICJOUyBvciBGQyA8IDAuMyINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yIDwtIGZhY3RvcihmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMC4zIiwgIlAgPCAwLjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZEUiA8IDAuMDUiLCAiRkRSIDwgMC4wMDEiKSkNCg0KcGx0IDwtIGh0bWx0b29sczo6dGFnTGlzdCgpDQpjb3VudGVyID0gMQ0KZm9yKGMgaW4gdW5pcXVlKGZnc2VhX3Jlc3VsdHNfYWxsJENvbnRyYXN0KSApIHsNCiAgZmdzZWFfcmVzdWx0c19zbGljZSA9IGZnc2VhX3Jlc3VsdHNfYWxsW2Znc2VhX3Jlc3VsdHNfYWxsJENvbnRyYXN0ID09IGMsXQ0KDQogICMgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAjICMgb3JkZXIgZ2VuZXMgZm9yIGNvbnZlbmllbmNlOg0KICBmZ3NlYV9yZXN1bHRzX3NsaWNlJGludmVydF9QIDwtICgtbG9nMTAoZmdzZWFfcmVzdWx0c19zbGljZSRwdmFsKSkgKiBzaWduKGZnc2VhX3Jlc3VsdHNfc2xpY2UkRVMpDQogIA0KICAjICNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQogICN0b3BfZmdzZWFfZyA8LSBjKCkNCiAgdG9wX2Znc2VhX2cgPC0gYyhmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ3BhdGh3YXknXVsNCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgICAgZmdzZWFfcmVzdWx0c19zbGljZVssICdwYXRod2F5J11bDQogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgDQogICMgZm9yIChjZWxsdHlwZSBpbiB1bmlxdWUobG1tX3Jlc3VsdHMkU3Vic2V0KSkgew0KICAjICAgICAgdG9wX2Znc2VhX2cgPC0gYyh0b3BfZmdzZWFfZywNCiAgIyAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbZmdzZWFfcmVzdWx0c19zbGljZSRTdWJzZXQ9PWNlbGx0eXBlLCAncGF0aHdheSddWw0KICAjICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MjBdXSwNCiAgIyAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbZmdzZWFfcmVzdWx0c19zbGljZSRTdWJzZXQ9PWNlbGx0eXBlLCAncGF0aHdheSddWw0KICAjICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjIwXV0pDQogICMgfQ0KICANCiAgdG9wX2Znc2VhX2cgPC0gdW5pcXVlKHRvcF9mZ3NlYV9nKQ0KICAjZmdzZWFfcmVzdWx0c19zbGljZSA8LSBmZ3NlYV9yZXN1bHRzX3NsaWNlWywgLTEqbmNvbChmZ3NlYV9yZXN1bHRzX3NsaWNlKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgDQogICMgR3JhcGggcmVzdWx0cw0KICBkeW5wbG90IDwtIGdncGxvdChmZ3NlYV9yZXN1bHRzX3NsaWNlLA0KICAgICAgICAgYWVzKHggPSBORVMsIHkgPSAtbG9nMTAocHZhbCksDQogICAgICAgICAgICAgY29sb3IgPSBDb2xvciwgbGFiZWwgPSBwYXRod2F5KSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhmY190aHJlc2hvbGQsLWZjX3RocmVzaG9sZCksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgbGFicyh4ID0gcGFzdGUoYywgIiBGQyIpLA0KICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuM2AgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkgKw0KICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQoZmdzZWFfcmVzdWx0c19zbGljZSwgQ29sb3IgPT0gIlAgPCAwLjA1IiB8IENvbG9yID09ICJGRFIgPCAwLjA1IiB8IENvbG9yID09ICJGRFIgPCAwLjAwMSIpLA0KICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTUsDQogICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDUwKSArDQogICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICMrDQogICAgICAjZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgIA0KICBwbHRbW2NvdW50ZXJdXSA8LSBhc193aWRnZXQoZ2dwbG90bHkoZHlucGxvdCkpDQogIGNvdW50ZXIgPC0gY291bnRlciArIDENCiAgI2RhdGF0YWJsZShzdWJzZXQocmVzdWx0cywgR2VuZSAlaW4lIEdPSSksIHJvd25hbWVzPUZBTFNFLGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikpDQp9DQpwbHQNCmBgYA0KDQojIDguNiBWaXN1YWxpemUgS0VHRyBwYXRod2F5DQoNCkdldCBwYXRod2F5IHZpc3VhbGl6YXRpb24gZnJvbSBLRUdHLiBjbHVzdGVyUHJvZmlsZXIgZ3JhYnMgdGhlIEtFR0cgcGF0aHdheSBJRHMgYW5kIGNvbWJpbmVkIHdpdGggcGF0aHZpZXcgaXQgY2FuIGRvd25sb2FkIGEgcGF0aHdheSBpbWFnZSBmcm9tIHRoZSBLRUdHIGRhdGFiYXNlLg0KVGhlIExNTSByZXN1bHRzIGFyZSBnaXZlbiB3aXRoIHRoZSBwYXRod2F5IGlkIHRvIHZpc3VhbGl6ZSB1cCBhbmQgZG93biByZWd1bGF0ZWQgZ2VuZXMuDQoNCmBgYHtyfQ0KZ2V0X3dwX2dtdGZpbGUgPC0gZnVuY3Rpb24oKSB7DQogICAgd3B1cmwgPC0gJ2h0dHBzOi8vd2lraXBhdGh3YXlzLWRhdGEud21jbG91ZC5vcmcvY3VycmVudC9nbXQvJw0KICAgIHggPC0gcmVhZExpbmVzKHdwdXJsKQ0KICAgIHkgPC0geFtncmVwKCdcXC5nbXQnLHgpXQ0KICAgIHN1YigiLiood2lraXBhdGh3YXlzLS4qXFwuZ210KS4qIiwgIlxcMSIsICB5W2dyZXAoJ0ZpbGUnLCB5KV0pDQp9DQoNCmdldF93cF9kYXRhIDwtIGZ1bmN0aW9uKG9yZ2FuaXNtKSB7DQogICAgb3JnYW5pc20gPC0gc3ViKCIgIiwgIl8iLCBvcmdhbmlzbSkNCiAgICBnbXRmaWxlIDwtIGdldF93cF9nbXRmaWxlKCkNCiAgICB3cHVybCA8LSAnaHR0cHM6Ly93aWtpcGF0aHdheXMtZGF0YS53bWNsb3VkLm9yZy9jdXJyZW50L2dtdC8nDQogICAgdXJsIDwtIHBhc3RlMCh3cHVybCwNCiAgICAgICAgICAgICAgICAgIGdtdGZpbGVbZ3JlcChvcmdhbmlzbSwgZ210ZmlsZSldKQ0KICAgIGYgPC0gdGVtcGZpbGUoZmlsZWV4dCA9ICIuZ210IikNCiAgICBkbCA8LSBteWRvd25sb2FkKHVybCwgZGVzdGZpbGUgPSBmKQ0KICAgIGlmIChpcy5udWxsKGYpKSB7DQogICAgICAgIG1lc3NhZ2UoImZhaWwgdG8gZG93bmxvYWQgd2lraVBhdGh3YXlzIGRhdGEuLi4iKQ0KICAgICAgICByZXR1cm4oTlVMTCkNCiAgICB9DQogICAgcmVhZC5nbXQud3AoZikNCn0NCmBgYA0KDQoNCmBgYHtyIGdldCBvbmxpbmUgYXZhaWxhYmxlIHBhdGh3YXlzLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgZW5yaWNoS0VHRyBncmFiIG9ubGluZSBwYXRod2F5cw0KZ2VuZSA8LSAgdGFyZ2V0X0RhdGFAZmVhdHVyZURhdGFAZGF0YVtbIkdlbmVJRCJdXQ0Ka2sgPC0gZW5yaWNoS0VHRyhnZW5lICAgICAgICAgPSBnZW5lLA0KICAgICAgICAgICAgICAgICBvcmdhbmlzbSAgICAgPSAnaHNhJywNCiAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4xKQ0KDQplbnJpY2hLRUdHX3Jlc3VsdHMgPC0ga2tAcmVzdWx0DQoNCiMgTWF0Y2ggY29sdW1uIHZhbHVlcyBmb3IgbGluaw0KIyBmc2dlYSBydW4NCmZnc2VhX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gZ3N1YigiS0VHR18iLCAiIiwgZmdzZWFfcmVzdWx0cyRwYXRod2F5KQ0KZmdzZWFfcmVzdWx0cyREZXNjcmlwdGlvbiA8LSBnc3ViKCJfIiwgIiAiLCBmZ3NlYV9yZXN1bHRzJERlc2NyaXB0aW9uKQ0KIyBzc2dlYSBydW4NCiMgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkRGVzY3JpcHRpb24gPC0gZ3N1YigiS0VHR18iLCAiIiwgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkUGF0aHdheSkNCiMgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkRGVzY3JpcHRpb24gPC0gZ3N1YigiXyIsICIgIiwgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkRGVzY3JpcHRpb24pDQoNCmVucmljaEtFR0dfcmVzdWx0cyREZXNjcmlwdGlvbiA8LSB0b3VwcGVyKGVucmljaEtFR0dfcmVzdWx0cyREZXNjcmlwdGlvbikNCg0KIyBDcmVhdGUgZGYgZm9yIG1hdGNoaW5nIEtFR0cgcmVzdWx0cw0KS0VHR0lEcyA8LSBmZ3NlYV9yZXN1bHRzI1tmZ3NlYV9yZXN1bHRzJENvbnRyYXN0ID09ICJQcm9fVHViIC0gRGlzX1R1YiJdDQpLRUdHSURzIDwtIEtFR0dJRHMgJT4lIGlubmVyX2pvaW4oZW5yaWNoS0VHR19yZXN1bHRzLCBieSA9ICdEZXNjcmlwdGlvbicpICU+JSBzZWxlY3QoRGVzY3JpcHRpb24sIElELCBnZW5lSUQsIGxlYWRpbmdFZGdlLCBwYWRqKQ0KDQojIENyZWF0ZSBnZW5lTGlzdCB3aXRoIERFIHJlc3VsdHMgZm9yIHZpc3VhbGl6YXRpb24gcGF0aHdheSBnZW5lcyANCmdlbmVsaXN0IDwtIGxtbV9yZXN1bHRzJEVzdGltYXRlDQpuYW1lcyhnZW5lbGlzdCkgPC0gdGFyZ2V0X0RhdGFAZmVhdHVyZURhdGFAZGF0YVtbIkdlbmVJRCJdXQ0KDQojIENob29zZSBtYW51YWwgb3IgaGlnaGVzdCBzaWduaWZpY2FudCBwYXRod2F5DQpwYXRod2F5X25hbWUgPC0gIklOU1VMSU4gU0lHTkFMSU5HIFBBVEhXQVkiICMgbWFudWFsIHBhdGh3YXkgcXVlcnkNCnBhdGh3YXlfbmFtZSA8LSB0b3VwcGVyKHBhdGh3YXlfbmFtZSkNCmlmIChwYXRod2F5X25hbWUgPT0gIiIpIHsNCiAgcGF0aHdheV9uYW1lIDwtIEtFR0dJRHNbb3JkZXIoS0VHR0lEcyRwYWRqLCBkZWNyZWFzaW5nID0gRkFMU0UpLCBdWzFdJERlc2NyaXB0aW9uDQp9DQpwYXRod2F5aWQgPC0gS0VHR0lEcyRJRFtLRUdHSURzJERlc2NyaXB0aW9uID09IHBhdGh3YXlfbmFtZV0NCg0KIyBHcmFiIHBhdGh3YXkgaW1hZ2Ugd2l0aCBnZW5lIGluZm8sIHNhdmUgYW5kIHBsb3QNCnZpZXdQYXRoIDwtIHBhdGh2aWV3KGdlbmUuZGF0YSAgPSBnZW5lbGlzdCwNCiAgICAgICAgICAgICAgICAgICAgIHBhdGh3YXkuaWQgPSBwYXRod2F5aWQsDQogICAgICAgICAgICAgICAgICAgICBzcGVjaWVzICAgID0gImhzYSIsDQogICAgICAgICAgICAgICAgICAgICBvdXQuc3VmZml4ID0gImdlbmVzaW5mbyIsIGtlZ2cubmF0aXZlID0gVCwgc2FtZS5sYXllciA9IEYpDQppbWcgPC0gcmVhZFBORyhwYXN0ZShwYXRod2F5aWQsICIuZ2VuZXNpbmZvLnBuZyIsIHNlcCA9ICIiKSkNCmdyaWQ6OmdyaWQucmFzdGVyKGltZykNCmBgYA0KDQojIyBCYXJwbG90IHBhdGh3YXlzDQoNCkNsZWFyIHZpenVhbGl6YXRpb24gb2YgdGhlIHRvcCAxNSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgZXhwcmVzc2VkIFJlYWN0b21lIHBhdGh3YXlzLiBQYXRod2F5cyB3aXRoIGEgYWRqdXN0ZWQgcC12YWx1ZSBvZiAwLjA1IG9yIGhpZ2hlciB3aWxsIGJlIHByZXNlbnRlZCBhcyByZWQgDQphbmQgYmVsb3cgMC4wNSBhcyBncmVlbi4NCg0KYGBge3IgaG9yaXpvbnRhbCBwYXRod2F5IGJhciBwbG90LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCnRvcF9hbGwgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZShmZ3NlYV9yZWFjdG9tZSRDb250cmFzdCkpIHsNCiAgICAjIGFjdGl2ZV9ncm91cDEgPC0gY29udHJhc3RfbGlzdFtjb250cmFzdF1bWzFdXVtbMV1dDQogICAgIyBhY3RpdmVfZ3JvdXAyIDwtIGNvbnRyYXN0X2xpc3RbY29udHJhc3RdW1sxXV1bWzJdXQ0KICAgICMgaW5kIDwtIHBEYXRhKHRhcmdldF9EYXRhKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMiA9PSBhY3RpdmVfZ3JvdXAxIHwgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIgPT0gYWN0aXZlX2dyb3VwMl0NCiAgICB0b3AgPC0gZmdzZWFfcmVhY3RvbWVbZmdzZWFfcmVhY3RvbWUkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgdG9wcG9zIDwtIHRvcFtvcmRlcih0b3AkTkVTLCBkZWNyZWFzaW5nID0gVFJVRSldWzA6MTVdDQogICAgdG9wbmVnIDwtIHRvcFtvcmRlcih0b3AkTkVTLCBkZWNyZWFzaW5nID0gRkFMU0UpXVswOjE1XQ0KICAgIHRvcCA8LSByYmluZCh0b3Bwb3MsIHRvcG5lZykNCiAgICANCiAgICB0b3AkQ29sb3JbdG9wJHBhZGogPCAwLjA1XSA8LSAicGFkaiA8IDAuMDUiDQogICAgdG9wJENvbG9yW3RvcCRwYWRqID09IDAuMDUgfCB0b3AkcGFkaiA+IDAuMDVdIDwtICJwYWRqID4gMC4wNSINCiAgICB0b3BfYWxsIDwtIHJiaW5kKHRvcF9hbGwsIHRvcCkNCiAgICBiYXJwbG90IDwtIGdncGxvdCh0b3AsYWVzKHg9cmVvcmRlcihwYXRod2F5LCBORVMpLHk9TkVTLGZpbGw9Q29sb3IpKSArIA0KICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgDQogICAgICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMyRUNDNzEiLCAiI0U3NEMzQyIpKSArDQogICAgICBnZ3RpdGxlKHBhc3RlKCJUb3AgMTUgKyBhbmQgLSBORVMgcmVhY3RvbWUgcGF0aHdheXMgZnJvbSIsIGNvbnRyYXN0KSkgKyANCiAgICAgIHhsYWIoInBhdGh3YXkiKSArIHlsYWIoIk5vcm1hbGl6ZWQgRW5yaWNobWVudCBTY29yZSIpICsNCiAgICAgIGNvb3JkX2ZsaXAoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogICAgICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQ0KICAgICAgI2ZhY2V0X3dyYXAofkNvbnRyYXN0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgICBwcmludChiYXJwbG90KQ0KfQ0KYGBgDQoNCiMgOSBTcGF0aWFsIERlY29udm9sdXRpb24NCg0KIyMgOS4xIENhbGN1bGF0ZSBiYWNrZ3JvdW5kcw0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX2JnfQ0KI2djKCkNCmJnID0gZGVyaXZlX0dlb014X2JhY2tncm91bmQoDQogIG5vcm0gPSBhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gInFfbm9ybSIpLA0KICBwcm9iZXBvb2wgPSBmRGF0YSh0YXJnZXRfRGF0YSkkTW9kdWxlLA0KICBuZWduYW1lcyA9IGMoIk5lZ1Byb2JlLUNUUDAxIiwiTmVnUHJvYmUtS2lsbyIsIk5lZ2F0aXZlIFByb2JlIiwgIk5lZ1Byb2JlLVdUWCIgKSkNCiAgI25lZ25hbWVzID0gIk5lZ1Byb2JlLVdUWCIpDQoNCmBgYA0KDQojIyA5LjIgTG9hZCBjZWxsIHByb2ZpbGUNCg0KQSAiY2VsbCBwcm9maWxlIG1hdHJpeCIgaXMgYSBwcmUtZGVmaW5lZCBtYXRyaXggdGhhdCBzcGVjaWZpZXMgdGhlDQpleHBlY3RlZCBleHByZXNzaW9uIHByb2ZpbGVzIG9mIGVhY2ggY2VsbCB0eXBlIGluIHRoZSBleHBlcmltZW50LiBUaGUNClNwYXRpYWxEZWNvbiBsaWJyYXJ5IGNvbWVzIHdpdGggb25lIHN1Y2ggbWF0cml4IHByZS1sb2FkZWQsIHRoZQ0KIlNhZmVUTUUiIG1hdHJpeCwgZGVzaWduZWQgZm9yIGVzdGltYXRpb24gb2YgaW1tdW5lIGFuZCBzdHJvbWEgY2VsbHMgaW4NCnRoZSB0dW1vciBtaWNyb2Vudmlyb25tZW50LiAoVGhpcyBtYXRyaXggd2FzIGRlc2lnbmVkIHRvIGF2b2lkIGdlbmVzDQpjb21tb25seSBleHByZXNzZWQgYnkgY2FuY2VyIGNlbGxzOyBzZWUgdGhlIFNwYXRpYWxEZWNvbiBtYW51c2NyaXB0IGZvcg0KZGV0YWlscy4pLiBPdGhlcndpc2UsIGxvYWQgc3BlY2lmaWMgcHJvZmlsZXMgZnJvbQ0KPGh0dHBzOi8vZ2l0aHViLmNvbS9OYW5vc3RyaW5nLUJpb3N0YXRzL0NlbGxQcm9maWxlTGlicmFyeS90cmVlL05ld1Byb2ZpbGVNYXRyaWNlcz4NCg0KYGBge3IgbG9hZF9jZWxsX3Byb2ZpbGVzfQ0KI3NhZmVUTUUNCmRhdGEoInNhZmVUTUUiKQ0KZGF0YSgic2FmZVRNRS5tYXRjaGVzIikNCmN1cnJlbnRfY2VsbF9wcm9maWxlPC1zYWZlVE1FDQoNCiNzZWU6IGh0dHBzOi8vZ2l0aHViLmNvbS9OYW5vc3RyaW5nLUJpb3N0YXRzL0NlbGxQcm9maWxlTGlicmFyeS90cmVlL05ld1Byb2ZpbGVNYXRyaWNlcw0KDQpjdXJyZW50X2NlbGxfcHJvZmlsZSA8LSBkb3dubG9hZF9wcm9maWxlX21hdHJpeChzcGVjaWVzID0gIkh1bWFuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlX2dyb3VwID0gIkFkdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0cml4bmFtZSA9ICJLaWRuZXlfSENBIikNCg0KaGVhdG1hcChzd2VlcChjdXJyZW50X2NlbGxfcHJvZmlsZSwgMSwgYXBwbHkoY3VycmVudF9jZWxsX3Byb2ZpbGUsIDEsIG1heCksICIvIiksDQogICAgICAgIGxhYlJvdyA9IE5BLCBtYXJnaW5zID0gYygxMCwgNSksIGNleENvbCA9IDAuNykNCmBgYA0KDQojIDkuMyBSdW4gc3BhdGlhbCBkZWNvbnZvbHV0aW9uDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcnVufQ0KIyB2ZWN0b3IgaWRlbnRpZnlpbmcgcHVyZSB0dW1vciBzZWdtZW50czoNCiN0YXJnZXRfRGF0YSRpc3R1bW9yID0gdGFyZ2V0X0RhdGEkQU5OMyA9PSAiQ09SRSIgJiB0YXJnZXRfRGF0YSRBTk4xID09ICJQYW5DSysiDQpyZXMgPSBydW5zcGF0aWFsZGVjb24ob2JqZWN0ID0gdGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgbm9ybV9lbHQgPSAicV9ub3JtIiwNCiAgICAgICAgICAgICAgICAgICAgICByYXdfZWx0ID0gImV4cHJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAjaXNfcHVyZV90dW1vciA9IHRhcmdldF9EYXRhJGlzdHVtb3IsDQogICAgICAgICAgICAgICAgICAgICAgY2VsbF9jb3VudHMgPSB0YXJnZXRfRGF0YSRudWNsZWksDQogICAgICAgICAgICAgICAgICAgICAgWCA9IGN1cnJlbnRfY2VsbF9wcm9maWxlLA0KICAgICAgICAgICAgICAgICAgICAgICNjZWxsbWVyZ2VzID0gc2FmZVRNRS5tYXRjaGVzLCAgICAgICAgICAgICAgIyBzYWZlVE1FLm1hdGNoZXMgb2JqZWN0LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAjbl90dW1vcl9jbHVzdGVycyA9IDUsICAgICAgICAgICAgICAgICAgICAgICMgaG93IG1hbnkgZGlzdGluY3QgdHVtb3IgcHJvZmlsZXMgdG8gYXBwZW5kIHRvIHNhZmVUTUUNCiAgICAgICAgICAgICAgICAgICAgICBhbGlnbl9nZW5lcyA9IFRSVUUpDQoNCg0KYGBgDQoNCiMgOS4zLjEgU3BhdGlhbCBkZWNvbnZvbHV0aW9uIGhlYXRtYXBzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCiMjIEFidW5kYW5jZQ0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX2hlYXRtYXAsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBOT1RFOiBjaGVjayBjbHVzdGVyaW5nLi4gd2h5IGRpZmZlcmVudD8NCg0KI3NldCBkaXNwbGF5IHRocmVzaG9sZHMNCnRocmVzaCA8LSBzaWduaWYocXVhbnRpbGUocmVzJGJldGEsIDAuOTcpLCAyKQ0KDQojIHBsb3Qgc3RvcmVkIHRvIGtlZXAgY2x1c3RlcmluZyBmb3IgbGF0ZXINCnAxPC1waGVhdG1hcChwbWluKHQocmVzJGJldGEpLHRocmVzaCksDQogICAgICAgICAjc2NhbGUgPSAicm93IiwgDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgICBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGFuZ2xlX2NvbCA9ICI5MCIsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgICNjbHVzdGVyaW5nX21ldGhvZCA9ICJhdmVyYWdlIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBsZWdlbmRfYnJlYWtzID0gYyhyb3VuZChzZXEoMCwgdGhyZXNoLCBsZW5ndGgub3V0ID0gNSkpWy01XSwgdGhyZXNoKSwNCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKHJvdW5kKHNlcSgwLCB0aHJlc2gsIGxlbmd0aC5vdXQgPSA1KSlbLTVdLCBwYXN0ZTAoIkFidW5kYW5jZSBzY29yZXMsXG50cnVuY2F0ZWQgYWJvdmUgYXQgIiwgdGhyZXNoKSksDQogICAgICAgICAjYnJlYWtzID0gc2VxKDAsIDUsIDEpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwiZGFya2JsdWUiKSkoMTAwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXQ0KICAgICAgICAgKQ0KI3AxDQpgYGANCg0KIyMgUHJvcG9ydGlvbmFsDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcHJvcGhlYXRtYXAsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBwcm9wb3J0aW9uczoNCnByb3BzIDwtIHJlcGxhY2UocmVzJHByb3Bfb2Zfbm9udHVtb3IsIGlzLm5hKHJlcyRwcm9wX29mX25vbnR1bW9yKSwgMCkNCg0KcDI8LXBoZWF0bWFwKHQocHJvcHMpLA0KICAgICAgICAgI3NjYWxlID0gInJvdyIsIA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBhbmdsZV9jb2wgPSAiOTAiLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICAjY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IHJvdW5kKHNlcSgwLCBtYXgocHJvcHMpICogMC45OSwgbGVuZ3RoLm91dCA9IDUpLCAyKSwNCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKHJvdW5kKHNlcSgwLCBtYXgocHJvcHMpLCBsZW5ndGgub3V0ID0gNSksIDIpWy01XSwgIlByb3BvcnRpb24gb2YgYWxsXG5maXR0ZWQgcG9wdWxhdGlvbnMiKSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsImRhcmtibHVlIikpKDEwMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQoNCiNwMg0KDQpgYGANCg0KIyMgU2NhbGVkDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fc2NhbGVkaGVhdG1hcCwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTV9DQojIHNjYWxlZCBhYnVuZGFuY2VzOg0KZXBzaWxvbiA8LSBtaW4ocmVzJGJldGFbcmVzJGJldGEgPiAwXSkNCm1hdCA8LSBzd2VlcChyZXMkYmV0YSwgMSwgcG1heChhcHBseShyZXMkYmV0YSwgMSwgbWF4KSwgZXBzaWxvbiksICIvIikNCg0KcGhlYXRtYXAodChtYXQpLA0KICAgICAgICAgI3NjYWxlID0gInJvdyIsDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgICBjdXRyZWVfcm93cyA9IDMsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGFuZ2xlX2NvbCA9ICI5MCIsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgICNjbHVzdGVyaW5nX21ldGhvZCA9ICJhdmVyYWdlIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBsZWdlbmRfYnJlYWtzID0gYyhyb3VuZChzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDUpLCAyKVstNV0sIDEpLA0KICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMocm91bmQoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1KSwgMilbLTVdLCAiU2NhbGVkIGFidW5kYW5jZVxuKHJhdGlvIHRvIG1heCkiKSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsImRhcmtibHVlIikpKDEwMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQoNCmBgYA0KDQojIDkuNCBCYXJwbG90cyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQojIyBhYnVuZGFuY2UNCg0KYGBge3IgU0RfYWJ1bmRhbmNlX2JhcnBsb3QsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBkZWZpbmUgdmFyaWFibGVzIHRvIHNob3cgaW4gaGVhdG1hcHM6DQpwRGF0YSh0YXJnZXRfRGF0YSkkcmVnaW9uIDwtIA0KICAgIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMywgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjMpKSAgICMgTXVzdCBiZSBtYW51YWw/DQpwRGF0YSh0YXJnZXRfRGF0YSkkY2xhc3MgPC0gDQogICAgZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4xLCB1bmlxdWUodGFyZ2V0X0RhdGEkQU5OMSkpICAgIyBNdXN0IGJlIG1hbnVhbD8NCg0KdmFyaWFibGVzX3RvX3Bsb3QgPC0gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKSAgICAgICAgICAgICAgICAgICAgICMgTXVzdCBiZSBtYW51YWw/DQoNCiNkZWZpbmUgY29sb3JzDQoNCiMgb25seSBmb3Igc2FmZVRNRSBjb2xvcnMNCiNjb2wgPC0gY2VsbGNvbHMNCg0KI2N1c3RvbSBjZWxtYXRyaXgNCiNnZXQgbGFyZ2UgbnVtYmVyIG9mIGNvbG9ycw0KcXVhbF9jb2xfcGFscyA9IGJyZXdlci5wYWwuaW5mb1ticmV3ZXIucGFsLmluZm8kY2F0ZWdvcnkgPT0gJ3F1YWwnLF0NCmNvbF92ZWN0b3IgPSB1bmlxdWUodW5saXN0KG1hcHBseShicmV3ZXIucGFsLCBxdWFsX2NvbF9wYWxzJG1heGNvbG9ycywgcm93bmFtZXMocXVhbF9jb2xfcGFscykpKSkNCmNlbGx0eXBlczwtc2FtcGxlKGNvbF92ZWN0b3IsbGVuZ3RoKGNvbG5hbWVzKHJlcyRiZXRhKSkpDQpuYW1lcyhjZWxsdHlwZXMpPC1jb2xuYW1lcyhyZXMkYmV0YSkNCmNvbDwtY2VsbHR5cGVzDQoNCg0KI3RlbXBmaXggZm9yIGFiYnJldmlhdGVkIGFuZCBub3cgbWlzbWF0Y2hpbmcgYW5ub3RhdGlvbnMNCiNjYW4ganVzdCB1c2UgYW5uIGRhdGFmcmFtZT8NCiN0bXBhbm48LWNiaW5kKEFOTjEsQU5OMixTTikNCnRtcGFubiA8LSBwRGF0YSh0YXJnZXRfRGF0YSlbYW5uX25hbWVzXQ0KDQoNCg0KbGF5b3V0KG1hdHJpeChjKDEsIDIsIDMsIDMpLCBucm93ID0gMiksDQogICAgICAgd2lkdGhzID0gYygxMCwgMywgMTAsIDMpLA0KICAgICAgIGhlaWdodHMgPSBjKDEsIDgsIDEwKSwNCiAgICAgICkNCg0KcGFyKG1hciA9IGMoMCwgOC4yLCAwLCAwLjIpKQ0KcGxvdChwMSR0cmVlX2NvbCwgbGFiZWxzID0gRiwgbWFpbiA9ICIiLCB5bGFiID0gIiIsIHlheHQgPSAibiIpDQpwYXIobWFyID0gYygxNSwgOCwgMCwgMCkpDQoNCiMgZGF0YSB0byBwbG90Og0KbWF0IDwtIHQocmVzJGJldGEpWywgcDEkdHJlZV9jb2wkb3JkZXJdDQojIGluZmVyIHNjYWxlIG9mIG5lZ2F0aXZlIHktYXhpcyBmb3IgYW5ub3RhdGlvbiBjb2xvcmJhcnMNCnltaW4gPC0gLW1heChjb2xTdW1zKG1hdCkpICogMC4xNQ0KaWYgKCFpcy5maW5pdGUoeW1pbikpIHsNCiAgeW1pbiA8LSAwDQp9DQoNCiMgZHJhdyBiYXJwbG90Og0KYnAgPC0gYmFycGxvdChtYXQsDQogICAgICAgICAgICAgIGNleC5sYWIgPSAxLjUsDQogICAgICAgICAgICAgIGNvbCA9IGNvbCwgYm9yZGVyID0gTkEsDQogICAgICAgICAgICAgIGNleC5uYW1lcyA9IDEuMSwNCiAgICAgICAgICAgICAgbGFzID0gMiwgbWFpbiA9ICIiLCB5bGFiID0gIkFidW5kYW5jZSBzY29yZXMiLA0KICAgICAgICAgICAgICB5bGltID0gYyh5bWluLCBtYXgoY29sU3VtcyhtYXQpKSkNCikNCg0KDQojIGFkZCBjb2xvciBiYXJzIGZvciBhbm5vdGF0aW9ucw0KZm9yIChuYW1lIGluIHJldih2YXJpYWJsZXNfdG9fcGxvdCkpIHsNCiAgeXJhbmdlIDwtIHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAxKVttYXRjaChuYW1lLCB2YXJpYWJsZXNfdG9fcGxvdCkgKyBjKDAsIDEpXQ0KICB4d2lkdGggPC0gKGJwWzJdIC0gYnBbMV0pIC8gMg0KICBmb3IgKGkgaW4gMTpuY29sKG1hdCkpIHsNCiAgICByZWN0KGJwW2ldIC0geHdpZHRoLCB5cmFuZ2VbMl0sIGJwW2ldICsgeHdpZHRoLCB5cmFuZ2VbMV0sDQogICAgICAgICAjIGJvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bc2VnbWVudEFubm90YXRpb25zW21hdGNoKGNvbG5hbWVzKG1hdClbaV0sIHNlZ21lbnRBbm5vdGF0aW9ucyRzZWdtZW50SUQpLCBuYW1lXV0NCiAgICAgICAgICNib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW2FubltwMSR0cmVlX2NvbCRvcmRlcltpXSwgbmFtZV1dDQogICAgICAgICBib3JkZXIgPSBOQSwgY29sID0gY29sb3JfbGlzdFtbbmFtZV1dW3RtcGFubltjb2xuYW1lcyhtYXQpW2ldLCBuYW1lXV0NCiAgICApDQogIH0NCn0NCmF4aXMoMiwNCiAgICAgYXQgPSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMilbLWMoMSwgbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpXSwNCiAgICAgbGFzID0gMiwgbGFiZWxzID0gdmFyaWFibGVzX3RvX3Bsb3QsIGx0eSA9IDAsIGNleC5heGlzID0gMS4yDQopDQoNCiNkcmF3IGEgbGVnZW5kOg0KcGFyKG1hciA9IGMoMC4xLCAwLjEsIDAuMSwgMC4xKSkNCmZyYW1lKCkNCmxlZ2VuZGNvbHMgPC0gbGVnZW5kbmFtZXMgPC0gYygpDQojZm9yIChuYW1lIGluIHJldihuYW1lcyhhbm5fY29sb3JzKSkpIHsNCmZvciAobmFtZSBpbiBjKCJzbGlkZV9uYW1lIiwgIkFOTjEiLCAiQU5OMyIpKSB7DQogIGxlZ2VuZGNvbHMgPC0gYyhsZWdlbmRjb2xzLCBOQSwgY29sb3JfbGlzdFtbbmFtZV1dLCBOQSkNCiAgbGVnZW5kbmFtZXMgPC0gYyhsZWdlbmRuYW1lcywgbmFtZSwgbmFtZXMoY29sb3JfbGlzdFtbbmFtZV1dKSwgTkEpDQp9DQpsZWdlbmQoImNlbnRlciIsDQogICAgICAgcGNoID0gMTUsDQogICAgICAgY2V4ID0gMS41LA0KICAgICAgIGNvbCA9IGMobGVnZW5kY29scywgcmVwKE5BLCAxKSwgcmV2KGNvbCkpLA0KICAgICAgIGxlZ2VuZCA9IGMobGVnZW5kbmFtZXMsICJDZWxsIHR5cGUiLCByZXYobmFtZXMoY29sKSkpLA0KKQ0KYGBgDQoNCiMjIHByb3BvcnRpb25hbA0KDQpgYGB7ciBTRF9wcm9wX2JhcnBsb3QsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBkZWZpbmUgdmFyaWFibGVzIHRvIHNob3cgaW4gaGVhdG1hcHM6DQp2YXJpYWJsZXNfdG9fcGxvdCA8LSBjKCJzbGlkZV9uYW1lIiwgIkFOTjEiLCAiQU5OMyIpDQoNCmxheW91dChtYXRyaXgoYygxLCAyLCAzLCAzKSwgbnJvdyA9IDIpLA0KICAgICAgIHdpZHRocyA9IGMoMTAsIDMsIDEwLCAzKSwNCiAgICAgICBoZWlnaHRzID0gYygxLCA4LCAxMCksDQogICAgICApDQoNCnBhcihtYXIgPSBjKDAsIDguMiwgMCwgMC4yKSkNCnBsb3QocDIkdHJlZV9jb2wsIGxhYmVscyA9IEYsIG1haW4gPSAiIiwgeWxhYiA9ICIiLCB5YXh0ID0gIm4iKQ0KcGFyKG1hciA9IGMoMTUsIDgsIDAsIDApKQ0KDQojIGRhdGEgdG8gcGxvdDoNCm1hdCA8LSB0KHJlcyRwcm9wX29mX25vbnR1bW9yKVssIHAyJHRyZWVfY29sJG9yZGVyXQ0KICBtYXQgPC0gcmVwbGFjZShtYXQsIGlzLm5hKG1hdCksIDApDQogICMgaW5mZXIgc2NhbGUgb2YgbmVnYXRpdmUgeS1heGlzIGZvciBhbm5vdGF0aW9uIGNvbG9yYmFycw0KICB5bWluIDwtIC0wLjE1DQoNCiMgZHJhdyBiYXJwbG90Og0KYnAgPC0gYmFycGxvdChtYXQsDQogICAgICAgICAgICAgIGNleC5sYWIgPSAxLjUsDQogICAgICAgICAgICAgIGNvbCA9IGNvbCwgYm9yZGVyID0gTkEsDQogICAgICAgICAgICAgIGNleC5uYW1lcyA9IDEuMSwNCiAgICAgICAgICAgICAgbGFzID0gMiwgbWFpbiA9ICIiLCB5bGFiID0gIlByb3BvcnRpb24gb2YgZml0dGVkIGNlbGxzIiwNCiAgICAgICAgICAgICAgeWxpbSA9IGMoeW1pbiwgbWF4KGNvbFN1bXMobWF0KSkpDQopDQoNCiMgYWRkIGNvbG9yIGJhcnMgZm9yIGFubm90YXRpb25zDQpmb3IgKG5hbWUgaW4gcmV2KHZhcmlhYmxlc190b19wbG90KSkgew0KICB5cmFuZ2UgPC0gc2VxKHltaW4gLyAzLCB5bWluLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDEpW21hdGNoKG5hbWUsIHZhcmlhYmxlc190b19wbG90KSArIGMoMCwgMSldDQogIHh3aWR0aCA8LSAoYnBbMl0gLSBicFsxXSkgLyAyDQogIGZvciAoaSBpbiAxOm5jb2wobWF0KSkgew0KICAgIHJlY3QoYnBbaV0gLSB4d2lkdGgsIHlyYW5nZVsyXSwgYnBbaV0gKyB4d2lkdGgsIHlyYW5nZVsxXSwNCiAgICAgICAgICMgYm9yZGVyID0gTkEsIGNvbCA9IGFubl9jb2xvcnNbW25hbWVdXVtzZWdtZW50QW5ub3RhdGlvbnNbbWF0Y2goY29sbmFtZXMobWF0KVtpXSwgc2VnbWVudEFubm90YXRpb25zJHNlZ21lbnRJRCksIG5hbWVdXQ0KICAgICAgICAgI2JvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bYW5uW3AyJHRyZWVfY29sJG9yZGVyW2ldLCBuYW1lXV0NCiAgICAgICAgIGJvcmRlciA9IE5BLCBjb2wgPSBjb2xvcl9saXN0W1tuYW1lXV1bdG1wYW5uW2NvbG5hbWVzKG1hdClbaV0sIG5hbWVdXQ0KICAgICkNCiAgfQ0KfQ0KYXhpcygyLA0KICAgICBhdCA9IHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAyKVstYygxLCBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMildLA0KICAgICBsYXMgPSAyLCBsYWJlbHMgPSB2YXJpYWJsZXNfdG9fcGxvdCwgbHR5ID0gMCwgY2V4LmF4aXMgPSAxLjINCikNCg0KDQojZHJhdyBhIGxlZ2VuZDoNCnBhcihtYXIgPSBjKDAuMSwgMC4xLCAwLjEsIDAuMSkpDQpmcmFtZSgpDQpsZWdlbmRjb2xzIDwtIGxlZ2VuZG5hbWVzIDwtIGMoKQ0KI2ZvciAobmFtZSBpbiByZXYobmFtZXMoYW5uX2NvbG9ycykpKSB7DQpmb3IgKG5hbWUgaW4gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKSkgew0KICBsZWdlbmRjb2xzIDwtIGMobGVnZW5kY29scywgTkEsIGNvbG9yX2xpc3RbW25hbWVdXSwgTkEpDQogIGxlZ2VuZG5hbWVzIDwtIGMobGVnZW5kbmFtZXMsIG5hbWUsIG5hbWVzKGNvbG9yX2xpc3RbW25hbWVdXSksIE5BKQ0KfQ0KbGVnZW5kKCJjZW50ZXIiLA0KICAgICAgIHBjaCA9IDE1LA0KICAgICAgIGNleCA9IDEuNCwNCiAgICAgICBjb2wgPSBjKGxlZ2VuZGNvbHMsIHJlcChOQSwgMSksIHJldihjb2wpKSwNCiAgICAgICBsZWdlbmQgPSBjKGxlZ2VuZG5hbWVzLCAiQ2VsbCB0eXBlIiwgcmV2KG5hbWVzKGNvbCkpKSwNCikNCmBgYA0KDQojIyBCb3hwbG90cw0KDQpCb3hwbG90IHBlciBjZWxsIGZyb20gdGhlIHNwYXRpYWwgZGVjb252b2x1dGlvbi4gQ2hvb3NlIHlvdXIgYW5ub3RhdGlvbiBpbiB3aGljaCB5b3Ugd2FudCB0byBjb21wYXJlIChBTk4pLiANClRoZSBhbm5vdGF0aW9uIHdpbGwgYmUgZGl2aWRlZCBpbiBjb25kaXRpb25zIGFuZCBlYWNoIGNlbGwgd2l0aCBoYXZlIGEgYm94cGxvdCB3aGljaCBpbmNsdWRlIHRoZSBhYnVuZGFuY2Ugc2NvcmUgb2YgZWFjaCBjb25kaXRpb24uDQpBIHN0YXRpc3RpY2FsIHRlc3QgaGFzIGJlZW4gYWRkZWQgaW5zaWRlIHRoZSBib3hwbG90IHRvIGNoZWNrIGZvciBzaWduaWZpY2FuY2UuIEZlZWwgZnJlZSB0byBjaGFuY2UgdGhlIHRlc3QgdG8gZml0IHRoZSBkYXRhLg0KDQpzaWduaWZpY2FuY2UgY29kZSAgICAgICAgIHAtdmFsdWUNCiAgICoqKiAgICAgICAgICAgICAgICAgWzAsIDAuMDAxXQ0KICAgICoqICAgICAgICAgICAgICAoMC4wMDEsIDAuMDFdDQogICAgICogICAgICAgICAgICAgICAoMC4wMSwgMC4wNV0NCiAgICAgLiAgICAgICAgICAgICAgICAoMC4wNSwgMC4xXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICgwLjEsIDFdIA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTQwLCBmaWcud2lkdGg9NDAsIHdhcm5pbmc9RkFMU0V9DQpBTk4gPC0gIkFOTjEiICMgQU5OIGNvbHVtbiB3aXRoIGNvbmRpdGlvbnMNCmlmIChsZW5ndGgodW5pcXVlKHRhcmdldF9EYXRhW1tBTk5dXSkpID4gMikgew0KICB0ZXN0IDwtICJrcnVza2FsLnRlc3QiDQp9IGVsc2Ugew0KICB0ZXN0IDwtICJ3aWxjb3gudGVzdCINCn0NCg0KcmVzX2ZpbHRlciA8LSByZXNbIXJlc0BwaGVub0RhdGFAZGF0YVtbImJldGEiXV09PTBdDQpyZXNfZmlsdGVyIDwtIGFzLmRhdGEuZnJhbWUocmVzX2ZpbHRlciRiZXRhKQ0KcmVzX2ZpbHRlcltyZXNfZmlsdGVyID09IDBdIDwtIE5BDQpib3hwbG90X3ByZXAgPC0gcmVzX2ZpbHRlclsgLCBjb2xTdW1zKGlzLm5hKHJlc19maWx0ZXIpKSA8IG5yb3cocmVzX2ZpbHRlcildIA0KDQpib3hwbG90X3ByZXAkY29uZGl0aW9uIDwtIHJvd25hbWVzKGJveHBsb3RfcHJlcCkNCg0KZm9yICh2YWx1ZSBpbiB1bmlxdWUodGFyZ2V0X0RhdGFbW0FOTl1dKSkgew0KICBjb24gPC0gcm93bmFtZXMocERhdGEodGFyZ2V0X0RhdGEpW3BEYXRhKHRhcmdldF9EYXRhKVtbQU5OXV0gPT0gdmFsdWUsXSkNCiAgYm94cGxvdF9wcmVwJGNvbmRpdGlvbltib3hwbG90X3ByZXAkY29uZGl0aW9uICVpbiUgY29uXSA8LSB2YWx1ZQ0KfQ0KDQpib3hwbG90IDwtIG1lbHQoYm94cGxvdF9wcmVwKQ0KDQojIGZ1bmN0aW9uIGZvciBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIA0KZ2l2ZS5uIDwtIGZ1bmN0aW9uKHgpew0KICByZXR1cm4oZGF0YS5mcmFtZSh5ID0gLTEsIGxhYmVsID0gcGFzdGUwKCJuID0gIixsZW5ndGgoeCkpKSkNCiAjcmV0dXJuKGMoeSA9IG1lZGlhbih4KSoxLjA1LCBsYWJlbCA9IGxlbmd0aCh4KSkNCiMgZXhwZXJpbWVudCB3aXRoIHRoZSBtdWx0aXBsaWVyIHRvIGZpbmQgdGhlIHBlcmZlY3QgcG9zaXRpb24NCn0NCg0KZ2dwbG90KGJveHBsb3QsIGFlcyhmYWN0b3IoY29uZGl0aW9uKSwgdmFsdWUsIGZpbGwgPSBjb25kaXRpb24pKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZW9tX2ppdHRlcihwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIodz0wLjEsaD0wLjEpKSArDQogIGZhY2V0X3dyYXAofnZhcmlhYmxlLCBzY2FsZT0iZnJlZSIsIG5jb2wgPSA1KSArDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKw0KICByb3RhdGVfeF90ZXh0KGFuZ2xlID0gNDUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJTcGF0aWFsIERlY29udm9sdXRpb24iLA0KICAgIHggPSAiQ29uZGl0aW9ucyIsDQogICAgeSA9ICJBYnVuY2FuY2Ugc2NvcmUiKSArIA0KICBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gdGVzdCwgbGFiZWwueSA9IC0zLCBzaXplPTUpICsgDQogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLnNpZ25pZiIsIG1ldGhvZCA9ICJ0LnRlc3QiLA0KICAgICAgICAgICAgICAgICAgICAgcmVmLmdyb3VwID0gIi5hbGwuIiwgaGlkZS5ucyA9IFRSVUUsIHNpemU9NSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBnaXZlLm4sIGdlb20gPSAidGV4dCIsIHNpemUgPSA1KQ0KDQoNCiNzdGF0X2NvbXBhcmVfbWVhbnMoc2l6ZT01KQ0KYGBgDQoNCg0KIyAxMSBDTlYNCkNvcHkgTnVtYmVyIFZhcmlhdGlvbiBhbmFseXNpcyB3aXRoIHRoZSBSIHNvZnR3YXJlIHBhY2thZ2UgaW5mZXJDTlYgKGh0dHBzOi8vZ2l0aHViLmNvbS9icm9hZGluc3RpdHV0ZS9pbmZlckNOVi93aWtpKS4NCg0KSW5mZXJDTlYgaXMgdXNlZCB0byBleHBsb3JlIHR1bW9yIHNpbmdsZSBjZWxsIFJOQS1TZXEgZGF0YSB0byBpZGVudGlmeSBldmlkZW5jZSBmb3Igc29tYXRpYyBsYXJnZS1zY2FsZSBjaHJvbW9zb21hbCBjb3B5IG51bWJlciBhbHRlcmF0aW9ucywgc3VjaCBhcyBnYWlucyBvciBkZWxldGlvbnMgb2YgZW50aXJlIGNocm9tb3NvbWVzIG9yIGxhcmdlIHNlZ21lbnRzIG9mIGNocm9tb3NvbWVzLiBUaGlzIGlzIGRvbmUgYnkgZXhwbG9yaW5nIGV4cHJlc3Npb24gaW50ZW5zaXR5IG9mIGdlbmVzIGFjcm9zcyBwb3NpdGlvbnMgb2YgdHVtb3IgZ2Vub21lIGluIGNvbXBhcmlzb24gdG8gYSBzZXQgb2YgcmVmZXJlbmNlICdub3JtYWwnIGNlbGxzLiBBIGhlYXRtYXAgaXMgZ2VuZXJhdGVkIGlsbHVzdHJhdGluZyB0aGUgcmVsYXRpdmUgZXhwcmVzc2lvbiBpbnRlbnNpdGllcyBhY3Jvc3MgZWFjaCBjaHJvbW9zb21lLCBhbmQgaXQgb2Z0ZW4gYmVjb21lcyByZWFkaWx5IGFwcGFyZW50IGFzIHRvIHdoaWNoIHJlZ2lvbnMgb2YgdGhlIHR1bW9yIGdlbm9tZSBhcmUgb3Zlci1hYnVuZGFudCBvciBsZXNzLWFidW5kYW50IGFzIGNvbXBhcmVkIHRvIHRoYXQgb2Ygbm9ybWFsIGNlbGxzLg0KDQpQZXIgcGF0aWVudCBhIGNudiBhbmFseXNpcyB3aXRoIGEgcHJvdmlkZWQgcmVmZXJlbmNlIGdyb3VwLiBUaGUgZ2VuZSBvcmRlciBpcyBtYWRlIGZyb20gdGhlIHByb2plY3RzIC5wa2MgZmlsZS4NCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9uIHdoZXJlIHRoZSBDTlYgYW5hbHlzaXMgc2hvdWxkIGxvb2sgYXQgc3BlY2lmaWNhbGx5IGFzIGdyb3VwIGFuZCBzcGVjaWZ5IGEgc3ViZ3JvdXAgYXMgZ3JvdXBfZmlsdGVyIGlmIHRoZSBncm91cCBvZiBpbnRlcmVzdCBpcyBhIHN1Ymdyb3VwIGluc2lkZSB0aGUgYW5ub3RhdGlvbi4gRm9yIGV4YW1wbGUgaWYgdGhlIENOViBhbmFseXNpcyBuZWVkcyB0byBiZSBvbmx5IG9uIHR1bW9yIHJlZ2lvbnMgdGhlIGdyb3VwIHdvdWxkIGJlIEFOTlgoQU5OIGNvbHVtbiB3aXRoIHRoZSBpbmZvIGFib3V0IHRoZSB0dW1vciByZWdpb25zKSBhbmQgZ3JvdXBfZmlsdGVyIFBhbkNLKy4gDQpUaGUgYW5ub3RhdGlvbiB0aGF0IHJlZmVyZW5jZXMgdGhlIHBhdGllbnRzIGFzIHBhdGllbnRzLiBJZiB0aGVyZSBhcmUgbm8gcGF0aWVudHMgbGVhdmUgdGhlIHBhcmFtZXRlciBlbXB0eSBhcyAiIi4NClRoZSBhbm5vdGF0aW9uIHRoYXQgc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSByZXN1bHRzIGluY2x1ZGluZyBhIHJlZmVyZW5jZSBzZXQgYXMgY252X3RhcmdldC4NClRoaXMgd2lsbCBjcmVhdGUgcGF0aWVudCBzcGVjaWZpYyBmaWxlcyB3aXRoIGFsbCB0aGUgaW5mb3JtYXRpb24gaW5mZXJDTlYgbmVlZHMuDQoNCmBgYHtyIGNyZWF0ZSBjbnYgZmlsZXMsIGluY2x1ZGU9RkFMU0V9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmdyb3VwIDwtICIiDQpncm91cF9maWx0ZXIgPC0gIiINCg0KcGF0aWVudHMgPC0gIiINCg0KY252X3RhcmdldCA8LSAiQU5OMSINCnByaW50KHBhc3RlKCJTaG93IENOViBvbiIsIGNudl90YXJnZXQsICJ3aXRoIHRoZSBzcGVjaWZpY2l0eSBvbiB0aGUgZ3JvdXBzIChpZiBhbnkpOiIsIGdyb3VwLCAiOyIsIGdyb3VwX2ZpbHRlcikpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KaWYgKCFwYXRpZW50cyA9PSAiIikgew0KICBwYXRpZW50X2xpc3QgPC0gdW5pcXVlKHRhcmdldF9EYXRhW1twYXRpZW50c11dKQ0KfSBlbHNlew0KICBwYXRpZW50X2xpc3QgPC0gImR1bW15Ig0KfQ0KDQpmaWxlIDwtIHJlYWRMaW5lcyhQS0NGaWxlcykNCmZpbGUgPC0gZ3N1YignICcsICcnLA0KICAgICAgICBnc3ViKCciJywgJycsIGZpbGUpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgYW5kIHJlbW92ZSBzcGFjZQ0KDQpkaXNwbGF5IDwtIGZpbGVbZ3JlcGwoIkRpc3BsYXlOYW1lIiwgZmlsZSwgZml4ZWQgPSBUUlVFKV0gICAgICAgICAjIEdyYWIgZGlzcGxheSBuYW1lcw0KZGlzcGxheSA8LSBnc3ViKCdEaXNwbGF5TmFtZScsICIiLA0KICAgICAgICAgICBnc3ViKCciJywgIiIsDQogICAgICAgICAgIGdzdWIoJzonLCAiIiwNCiAgICAgICAgICAgZ3N1YignLCcsICIiLA0KICAgICAgICAgICBnc3ViKCcgJywgIiIsDQogICAgICAgICAgIGdzdWIoJ18wMScsICIiLCBkaXNwbGF5KSkpKSkpICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdyYWIgb25seSB0aGUgbmFtZXMNCmRpc3BsYXkgPC0gdW5pcXVlKGRpc3BsYXkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIGR1cGxpY2F0ZXMNCg0KY2hyIDwtIGZpbGVbZ3JlcCgiR2Vub21lQ29vcmRpbmF0ZXMiLCBmaWxlKSsxXSAgICAgICAgICAgICAgICAgICAgIyBHZXQgdGhlIGNociBwb3NpdGlvbnMgdW5kZXIgdGhlIEdlbm9tZUNvb3JkaW5hdGVzIGxpbmUNCmNociA8LWdzdWIoJywnLCAiIiwgY2hyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIHVud2FudGVkIHN5bWJvbHMNCg0KcG9zaXRpb25zIDwtIGRhdGEuZnJhbWUoDQogIG5hbWUgPSBkaXNwbGF5LA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTd2FwIHR5cGVzIHRvIHJlY29nbml6ZSBzcGxpdCBjb2x1bW5zIGFzIHNpbmd1bGFyIGNvbHVtbnMNCg0KcG9zaXRpb25zJG9yZGVyIDwtIGFzLm51bWVyaWMoZ3N1YignY2hyJywgJycsIHBvc2l0aW9ucyRjaHIuMSkpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zW29yZGVyKHBvc2l0aW9ucyRvcmRlciwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXSAjIE9yZGVyIHRoZSBjaHJvbW9zb21lcy4NCnBvc2l0aW9ucyRvcmRlciA8LSBOVUxMDQoNCmNvbG5hbWVzKHBvc2l0aW9ucykgPC0gTlVMTA0Kcm93bmFtZXMocG9zaXRpb25zKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbmZlckNOViByZXF1aXJlcyBubyBjb2x1bW4gYW5kIHJvdyBuYW1lcw0KDQp3cml0ZS50YWJsZShwb3NpdGlvbnMsICJnZW5lX29yZGVyX0hzX1dUQV92MV9wa2MudHh0IiwNCiAgICAgICAgICAgIHNlcCA9ICJcdCIsDQogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdyaXRlIGF3YXkNCg0KDQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIGlmIChncm91cF9maWx0ZXIgIT0gIiIpIHsNCiAgICBncm91cF9pbnRlcmVzdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YVtbIlNhbXBsZUlEIl1dW3RhcmdldF9EYXRhW1tncm91cF1dID09IGdyb3VwX2ZpbHRlciAmIHRhcmdldF9EYXRhW1twYXRpZW50c11dID09IHBhdGllbnRdDQogIH0gZWxzZSBpZiAobGVuZ3RoKHBhdGllbnRfbGlzdCkgPiAxKSB7DQogICAgZ3JvdXBfaW50ZXJlc3QgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGFbWyJTYW1wbGVJRCJdXVt0YXJnZXRfRGF0YVtbcGF0aWVudHNdXSA9PSBwYXRpZW50XQ0KICB9IGVsc2Ugew0KICAgIGdyb3VwX2ludGVyZXN0IDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhW1siU2FtcGxlSUQiXV0NCiAgfQ0KICANCiAgY291bnRzIDwtIHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dDQogIGNvbG5hbWVzKGNvdW50cykgPC0gZ3N1YignLmRjYycsICcnLGNvbG5hbWVzKGNvdW50cykpDQogIGNvdW50cyA8LSBjb3VudHNbLGNvbG5hbWVzKGNvdW50cykgJWluJSBjKGdyb3VwX2ludGVyZXN0KV0NCiAgd3JpdGUudGFibGUoY291bnRzLCBwYXN0ZTAocGF0aWVudCwgIi5Db3VudHMudHN2IiksIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KICBhbm5vdGF0aW9uIDwtIHRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhDQogIGFubm90YXRpb24kU2FtcGxlSUQgPC0gZ3N1YignLmRjYycsJycsIHJvd25hbWVzKGFubm90YXRpb24pKQ0KICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVJRCAlaW4lIGMoZ3JvdXBfaW50ZXJlc3QpLF0NCiAgI2Fubm90YXRpb24gPC0gYW5ub3RhdGlvbltjKCJTYW1wbGVJRCIsICJzZWdtZW50IildICAgICAgICAgICAgICAgICAgIyBTZWxlY3QgU2FtcGxlSUQgYW5kIHRoZSBuZWVkZWQgYW5ub3RhdGlvbiAoQ05WIHJlcXVpcmVzIFNhbXBsZUlEIGFuZCBvbmx5IDEgYW5ub3RhdGlvbikNCiAgYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgY252X3RhcmdldCldDQogIHJvd25hbWVzKGFubm90YXRpb24pIDwtIE5VTEwNCiAgY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQogIHdyaXRlLnRhYmxlKGFubm90YXRpb24sIHBhc3RlMChwYXRpZW50LCAiLkFubm90YXRpb25zLnRzdiIpLA0KICAgICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1ha2UgYW5ub3RhdGlvbiBmaWxlDQp9DQpgYGANCg0KU2VsZWN0IHRoZSByZWZlcmVuY2Ugc2V0IGluIHJlZmVyZW5jZS4gVGhpcyBjYW4gYmUgbW9yZSB0aGFuIG9uZS4gRXZlcnkgcGF0aWVudCB3aXRob3V0IGl0cyBvd24gcmVmZXJlbmNlIHdpbGwgbm90IGJlIGFuYWx5c2VkLg0KDQpgYGB7ciBydW4gQ05WLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyMjIyMjIyMjUEFSQU1FVEVSUyMjIyMjIyMjDQpyZWZlcmVuY2UgPC0gYygibm9ybWFsIikNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpmYWlsZWQgPC0gYygpDQpjbnZfbGlzdCA8LSBsaXN0KCkNCg0KZm9yIChwYXRpZW50IGluIHBhdGllbnRfbGlzdCkgew0KICAjIE5hbWUgb3V0cHV0IGZvbGRlcg0KICBvdXRfZGlyIDwtIHBhc3RlMChwYXRpZW50LCAiX0NOViIpDQogIGlmIChzdHJfZGV0ZWN0KHBhc3RlKHJlYWRMaW5lcyhwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSksIGNvbGxhcHNlID0gJycpLCByZWZlcmVuY2UpID09IEZBTFNFKSB7DQogICAgZmFpbGVkIDwtIGMoZmFpbGVkLCBwYXRpZW50KQ0KICAgIG5leHQNCiAgICB9DQoNCiAgIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KICBpbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0gcGFzdGUwKHBhdGllbnQsICIuQ291bnRzLnRzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uc19maWxlPSBwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9vcmRlcl9maWxlPSAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNnZW5lX29yZGVyX2ZpbGU9ICJnZW5jb2RlX3YxOV9nZW5lX3Bvcy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHJfZXhjbHVkZSA9IGMoImNock0iKSkjYygiY2hyTSIpKSAjIERlZmF1bHQgZXhjbHVkZXMgY2hyWCwgY2hyWSBhbmQgY2hyTS4gQnkgb25seSBwaWNraW5nIGNock0geW91IGluY2x1ZGUgdGhlIFggYW5kIFkgY2hyb21vc29tZXMuDQoNCiAgIyBwZXJmb3JtIGluZmVyY252IG9wZXJhdGlvbnMgdG8gcmV2ZWFsIGNudiBzaWduYWwuIEZvciBhbGwgb3B0aW9uczogaHR0cHM6Ly9yZHJyLmlvL2dpdGh1Yi9icm9hZGluc3RpdHV0ZS9pbmZlcmNudi9tYW4vcnVuLmh0bWwNCiAgI2Nudl9saXN0W1twYXRpZW50XV0NCiAgaW5mZXJjbnZfb2JqIDwtIGluZmVyY252OjpydW4oaW5mZXJjbnZfb2JqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0X2Rpcj0gb3V0X2RpciwgICMgZGlyIGlzIGF1dG8tY3JlYXRlZCBmb3Igc3RvcmluZyBvdXRwdXRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9ieV9ncm91cHM9RkFMU0UsICAgIyBjbHVzdGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVub2lzZT1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW1vcl9zdWJjbHVzdGVyX3BhcnRpdGlvbl9tZXRob2QgPSBjKCJxbm9ybSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5c2lzX21vZGUgPSAic3ViY2x1c3RlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vX3Bsb3Q9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMsZGVidWc9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICBwbG90X2NudihpbmZlcmNudl9vYmosDQogICAgICAgICAgb3V0X2RpciA9IHBhc3RlMChwYXRpZW50LCAiX0NOViIpLA0KICAgICAgICAgIHRpdGxlID0gImluZmVyQ05WIiwNCiAgICAgICAgICBvYnNfdGl0bGUgPSAiT2JzZXJ2YXRpb25zIChDZWxscykiLA0KICAgICAgICAgIHJlZl90aXRsZSA9ICJSZWZlcmVuY2VzIChDZWxscykiLA0KICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzID0gRkFMU0UsDQogICAgICAgICAgY2x1c3Rlcl9yZWZlcmVuY2VzID0gRkFMU0UsDQogICAgICAgICAgcGxvdF9jaHJfc2NhbGUgPSBGQUxTRSwNCiAgICAgICAgICAjY2hyX2xlbmd0aHMgPSBOVUxMLA0KICAgICAgICAgIGtfb2JzX2dyb3VwcyA9IDEsDQogICAgICAgICAgY29udGlnX2NleCA9IDEuNSwNCiAgICAgICAgICAjeC5jZW50ZXIgPSBtZWFuKGluZmVyY252X29iakBleHByLmRhdGEpLA0KICAgICAgICAgIHgucmFuZ2UgPSAiYXV0byIsDQogICAgICAgICAgI2hjbHVzdF9tZXRob2QgPSAid2FyZC5EIiwNCiAgICAgICAgICBvdXRwdXRfZmlsZW5hbWUgPSAiaW5mZXJjbnYiLA0KICAgICAgICAgIG91dHB1dF9mb3JtYXQgPSAicG5nIiwNCiAgICAgICAgICBwbmdfcmVzID0gMzAwDQogICAgICAgICAgKQ0KfQ0KYGBgDQoNCmBgYHtyIHNob3cgQ05WLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9MjV9DQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIHByaW50KHBhc3RlKGdyb3VwX2ZpbHRlciwgcGF0aWVudCkpDQogIGlmIChwYXRpZW50ICVpbiUgZmFpbGVkKSB7DQogICAgcHJpbnQoIiBeICAgIFBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQogIGltZyA8LSByZWFkUE5HKHBhc3RlKHBhc3RlMChwYXRpZW50LCAiX0NOViIpLCAiL2luZmVyY252LnBuZyIsIHNlcCA9ICIiKSkNCiAgZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCiAgZ3JpZDo6Z3JpZC5yYXN0ZXIoaW1nKQ0KfQ0KYGBgDQoNCiMgMTEuMSBEZW5kcm9ncmFtDQoNCkNsb3NlciBsb29rIGF0IHRoZSBkZW5kcm9ncmFtIGZyb20gdGhlIENOViBhbmFseXNpcyBwZXIgcGF0aWVudCB3aGVyZSB0aGUgbm9kZXMgYXMgd3JpdHRlbiBudW1iZXJzLiBVc2UgdGhlIG51bWJlcnMgdG8gc2VsZWN0IHN1Ymdyb3VwcyBmb3IgZnVydGhlciBhbmFseXNpcyBpbiAndC10ZXN0IG9uIHR3byBzdWJncm91cHMnLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQojb3V0X2RpciA8LSAiVDFfTkFOT18wMTJfQ05WIg0KdHJlZXMgPC0gbGlzdCgpDQoNCmZvciAocGF0aWVudCBpbiBwYXRpZW50X2xpc3QpIHsNCiAgaWYgKHBhdGllbnQgJWluJSBmYWlsZWQpIHsNCiAgICAjcHJpbnQoIlBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQp0cmVlIDwtIHJlYWQudHJlZShwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uc19kZW5kcm9ncmFtLnR4dCIsIHNlcCA9ICIiKSkNCm9idiA8LSByZWFkLmNzdihwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uX2dyb3VwaW5ncy50eHQiLCBzZXAgPSAiIiksIHNlcD0iIikNCnRyZWVzW1twYXRpZW50XV0gPC0gZ2d0cmVlKA0KICB0cmVlLCBsYWRkZXJpemU9RikgKw0KICBnZW9tX3RyZWVzY2FsZSgpICsNCiAgZ2VvbV90aXBsYWIoY29sb3I9b2J2JEFubm90YXRpb24uQ29sb3IsIGhqdXN0PS0uMikgKw0KICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICdvZmYnKSArDQogIHRoZW1lX3RyZWUyKHBsb3QubWFyZ2luPW1hcmdpbig2LCAyMDAsIDYsIDYpKSArDQogIGdlb21fdGV4dDIoYWVzKGxhYmVsPW5vZGUpLCBoanVzdD0tLjMsIHNpemUgPSAzKSArDQogIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXRpZW50KQ0KfQ0KDQpncmlkLmFycmFuZ2UoZ3JvYnM9dHJlZXMsbmNvbD0zKQ0KYGBgDQoNCiMjIHQtdGVzdCBjaHIgd2l0aCBncm91cHMNCg0KQ29tcGFyZSB0aGUgY29udHJhc3Qgd2l0aGluIGEgY2hyb21vc29tZS4gU2VsZWN0IGEgY2hyb21vc29tZSwgY29udHJhc3QsIGFuZCBwYXRpZW50IG9mIGludGVyZXN0IHRvIHJ1biBhIHQtdGVzdCBvbi4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNociA8LSAiY2hyWCIgIyBTZWxlY3QgY2hyb21vc29tZSBvZiBpbnRlcmVzdA0KY29udHJhc3QgPC0gYygibm9ybWFsIiwgIkRLRCIpICMgU2VsZWN0IGNvbnRyYXN0DQpwYXRpZW50IDwtICJkdW1teSINCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpwb3NpdGlvbnMgPC0gYXMuZGF0YS5mcmFtZShwb3NpdGlvbnMpDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIGMoImdlbmUiLCAiY2hyIiwgImJlZ2luIiwgImVuZCIpDQpzZWxlY3RfZ2VuZXMgPC0gcG9zaXRpb25zJGdlbmVbcG9zaXRpb25zJGNociA9PSBjaHJdICMgR3JhYiBjaHIgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb24NCg0KZmlsdGVyX2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImxvZ19xIl1dKQ0KY29sbmFtZXMoZmlsdGVyX2NvdW50cykgPC0gZ3N1YignLmRjYycsJycsIGNvbG5hbWVzKGZpbHRlcl9jb3VudHMpKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzWyxzZWxlY3Rfc2FtcGxlcyRTYW1wbGVfSURdICMgZmlsdGVyIG91dCBzYW1wbGVzIHRoYXQgYXJlIG5vdCB0aGUgaW50ZXJlc3RpbmcgcmVnaW9uDQoNCnNlbGVjdF9nZW5lcyA8LSBzZWxlY3RfZ2VuZXNbc2VsZWN0X2dlbmVzICVpbiUgcm93bmFtZXMoZmlsdGVyX2NvdW50cyldICMgT25seSB1c2UgZ2VuZXMgdGhhdCBhcmUgYWN0dWFsbHkgaW4gdGhlIGRhdGEgKHBrYyBnZW5lIGZpbGUgaGFzIGFsbCBvZiB0aGVtKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzW3NlbGVjdF9nZW5lcyxdICMgZmlsdGVyIG91dCBub24gY2hyb21vc29tZSBzcGVjaWZpYyBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IGNociwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTAgfQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCSCINCmNvdW50ZXI9MQ0KDQpsb2dfcV9maWx0ZXIgPC1hcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpDQoNCmNvbXBzX2RmPC1kYXRhLmZyYW1lKGNvbXA9JycsdmFsPScnKQ0KDQpmb3IgKGFjdGl2ZV9ncm91cDEgaW4gY29udHJhc3QpIHsNCiAgICBmb3IgKGFjdGl2ZV9ncm91cDIgaW4gY29udHJhc3QpIHsNCg0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgICNwcmludChjb21wKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQoNCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJEFOTj09YWN0aXZlX2dyb3VwMl1dDQoNCiAgICAgICNydW4gdF90ZXN0cw0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCg0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQoNCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZSggYXBwbHkobG9nX3FfZmlsdGVyLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkgKSApICkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCg0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCg0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRhZGpfcF92YWx1ZSA8IDAuMDVdIDwtICJQIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KICAgICAgcmVzdWx0cyRDb2xvclthYnMocmVzdWx0cyRGQykgPCAwLjVdIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuNSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCiAgICAgICN2dWxjYW5vcGxvdA0KDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCg0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzW2luZCwgJ0dlbmUnXVtvcmRlcihyZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIHBsb3RzW1tjb3VudGVyXV08LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgaW4iLCBjaHIsICItIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCg0KZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzLG5jb2w9MikNCmBgYA0KDQojIHQtdGVzdCBvbiB0d28gc3ViZ3JvdXBzDQoNClNlbGVjdCBub2RlcyBmcm9tIHRoZSBkZW5kcm9ncmFtIGFzIHN1Ymdyb3VwcywgaW4gdGhpcyBjYXNlIG5vZGVzIDE4IGFuZCAxNS4gRGVjaWRlIG9uIHRoZSBpbnZvbHZlZCBjaHJvbW9zb21lcyBhbmQgYWRkIHRoZW0gaW50byB0aGUgY2hyX2xpc3QuIFNwZWNpZnkgdGhlIHBhdGllbnQgaW4gcGF0aWVudC4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNocl9saXN0IDwtIGMoImNocjIyIikgIyBDaG9vc2UgbmVlZGVkIGNocm9tb3NvbWVzIG9mIHRoZSB0YXJnZXQgYXJlYS4gVGhpbmsgb2YgaXQgYXMgdGhlIHgtYXhpcw0KcGF0aWVudCA8LSAiZHVtbXkiDQpub2RlX2ludGVyZXN0IDwtIDIzMyAjIENob29zZSBzdWJncm91cCAxDQpub2RlX2ludGVyZXN0MiA8LSAyMTYgIyBDaG9vc2Ugc3ViZ3JvdXAgMg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCnRyZWUgPC0gcmVhZC50cmVlKHBhc3RlKHBhdGllbnQsIl9DTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zX2RlbmRyb2dyYW0udHh0Iiwgc2VwID0gIiIpKQ0KdHJlZV9pbmZvIDwtIHRyZWUgJT4lIGFzLnRyZWVkYXRhICU+JSBhc190aWJibGUgIyB0cmFuc2Zvcm0gdHJlZSBpbnRvIGFjY2Vzc2libGUgZGF0YQ0KDQpzYW1wbGVzX2ludGVyZXN0IDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QpICMgR2V0IGxhYmVscyBhdHRhY2hlZCB0byB0aGUgZ3JvdXANCnNhbXBsZXNfaW50ZXJlc3QgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdCRsYWJlbCkpICMgZm9ybWF0dGluZw0KcGFzdGUoIlN1Ymdyb3VwIDE6ICIsIHNhbXBsZXNfaW50ZXJlc3QpDQoNCnNhbXBsZXNfaW50ZXJlc3QyIDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QyKQ0Kc2FtcGxlc19pbnRlcmVzdDIgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdDIkbGFiZWwpKQ0KcGFzdGUoIlN1Ymdyb3VwIDI6ICIsIHNhbXBsZXNfaW50ZXJlc3QyKQ0KYGBgDQoNCmBgYHtyfQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKQ0KY29sbmFtZXMocG9zaXRpb25zKSA8LSBjKCJnZW5lIiwgImNociIsICJiZWdpbiIsICJlbmQiKSAjIEZvcm1hdHRpbmcNCg0Kc2VsZWN0X2dlbmVzIDwtIHBvc2l0aW9ucyRnZW5lW3Bvc2l0aW9ucyRjaHIgJWluJSBjaHJfbGlzdF0gIyB8IHBvc2l0aW9ucyRjaHIgPT0gY2hyMl0gIyBHcmFiIGNocm9tb3NvbWUgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0IHwgYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0MixdICMgR3JhYiBzdWJncm91cCBzcGVjaWZpYyBTYW1wbGUgSURzDQoNCmZpbHRlcl9jb3VudHMgPC0gdGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sibG9nX3EiXV0NCmNvbG5hbWVzKGZpbHRlcl9jb3VudHMpIDwtIGdzdWIoJy5kY2MnLCcnLCBjb2xuYW1lcyhmaWx0ZXJfY291bnRzKSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1ssc2VsZWN0X3NhbXBsZXMkU2FtcGxlX0lEXSAjIEZpbHRlciBTYW1wbGUgSUQncw0KDQpzZWxlY3RfZ2VuZXMgPC0gc2VsZWN0X2dlbmVzW3NlbGVjdF9nZW5lcyAlaW4lIHJvd25hbWVzKGZpbHRlcl9jb3VudHMpXSAjIE9ubHkgdXNlIGdlbmVzIHRoYXQgYXJlIGFjdHVhbGx5IGluIHRoZSBkYXRhIChwa2MgZ2VuZSBmaWxlIGhhcyBhbGwgb2YgdGhlbSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1tzZWxlY3RfZ2VuZXMsXSAjIEZpbHRlciBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IHN1Ymdyb3VwcywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQpwbG90czwtbGlzdCgpDQp0YWJsZXM8LWxpc3QoKQ0KbGFiZWxzPC1saXN0KCkNCnRlc3Q8LSJ0dGVzdCINCm10YzwtIkJIIg0KY291bnRlcj0xDQoNCmxvZ19xX2ZpbHRlciA8LWFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykNCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCg0KYWN0aXZlX2dyb3VwMSA8LSBzYW1wbGVzX2ludGVyZXN0ICNzdWJncm91cCAxDQphY3RpdmVfZ3JvdXAyIDwtIHNhbXBsZXNfaW50ZXJlc3QyICNzdWJncm91cCAyDQoNCiMgZm9yIChhY3RpdmVfZ3JvdXAxIGluIGMoInN1YjEiKSkgew0KIyAgICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIGMoInN1YjIiKSkgew0KDQogICAgICAjc3VwcmVzcyByZWR1bmNhbnQgY29tcGFyZXMNCiAgICAgICNpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgICNjb21wPC1wYXN0ZShzb3J0KGMoYWN0aXZlX2dyb3VwMSxhY3RpdmVfZ3JvdXAyKSksY29sbGFwc2UgPSAiXyIpDQogICAgICAjcHJpbnQoY29tcCkNCiAgICAgICNpZihjb21wICVpbiUgY29tcHNfZGYkY29tcCkge25leHR9DQogICAgICB0ZW1wX2RmPC1kYXRhLmZyYW1lKGNvbXA9Y29tcCAsdmFsPTEpDQogICAgICBjb21wc19kZjwtcmJpbmQoY29tcHNfZGYsdGVtcF9kZikNCg0KICAgICAgIyBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICAjIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgIyBncm91cDI8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkQU5OPT1hY3RpdmVfZ3JvdXAyXV0NCg0KICAgICAgbGFiZWxzW1tjb3VudGVyXV08LXBhc3RlKGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKQ0KICAgICAgZ3JvdXAxPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDJdXQ0KDQogICAgICAjcnVuIHRfdGVzdHMNCiAgICAgIHJlc3VsdHM8LWFzLmRhdGEuZnJhbWUgKCBhcHBseShsb2dfcV9maWx0ZXIsIDEsIGZ1bmN0aW9uKHgpIHQudGVzdCh4W2NvbG5hbWVzKGdyb3VwMSldLHhbY29sbmFtZXMoZ3JvdXAyKV0pJHAudmFsdWUpICkNCiAgICAgIGNvbG5hbWVzKHJlc3VsdHMpPC0icmF3X3BfdmFsdWUiDQoNCiAgICAgICNtdWx0aXBsZV90ZXN0aW5nX2NvcnJlY3Rpb24NCiAgICAgIGFkal9wX3ZhbHVlPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9bXRjKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxhZGpfcF92YWx1ZSkNCg0KICAgICAgI2NhbGNfZmRyDQogICAgICBGRFI8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD0iZmRyIikNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsRkRSKQ0KDQogICAgICAjZm9sZF9jaGFuZ2VzDQogICAgICAjYXMgYmFzZSBkYXRhIGlzIGFscmVhZHkgbG9nIHRyYW5zZm9ybWVkLCBtZWFucyBuZWVkIHRvIGJlIHN1YnRyYWN0ZWQgdG8gZ2V0IEZDIGluIGxvZyBzcGFjZQ0KICAgICAgZmNoYW5nZXM8LWFzLmRhdGEuZnJhbWUoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgKG1lYW4oeFtjb2xuYW1lcyhncm91cDEpXSkgLSBtZWFuKHhbY29sbmFtZXMoZ3JvdXAyKV0pICkgKSApDQogICAgICBjb2xuYW1lcyhmY2hhbmdlcyk8LSJGQyINCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsZmNoYW5nZXMpDQoNCiAgICAgICNhZGQgZ2VuZW5hbWVzDQogICAgICByZXN1bHRzJEdlbmU8LXJvd25hbWVzKHJlc3VsdHMpDQoNCiAgICAgICNzZXQgY2F0ZWdvcmllcyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KICAgICAgcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkYWRqX3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwgMC41XSA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yIDwtIGZhY3RvcihyZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjUiLCAiUCA8IDAuMDUiLCAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQogICAgICAjdnVsY2Fub3Bsb3QNCg0KICAgICAgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAgICAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQoNCiAgICAgIHJlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChyZXN1bHRzJGFkal9wX3ZhbHVlKSkgKiBzaWduKHJlc3VsdHMkRkMpDQogICAgICB0b3BfZyA8LSBjKCkNCiAgICAgIHRvcF9nIDwtIGModG9wX2csDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbaW5kLCAnR2VuZSddWw0KICAgICAgICAgICAgICAgICAgIG9yZGVyKHJlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTVdXSkNCiAgICAgIHRvcF9nPC0gdW5pcXVlKHRvcF9nKQ0KICAgICAgcmVzdWx0cyA8LSByZXN1bHRzWywgLTEqbmNvbChyZXN1bHRzKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCg0KICAgICAgIyBHcmFwaCByZXN1bHRzDQogICAgICAjcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgcCA8LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgZnJvbSIsIGNocl9saXN0LCAiaW4iLCAic3ViZ3JvdXAgMiIsIiA8LSBsb2cyKEZDKSAtPiBFbnJpY2hlZCBpbiIsICJzdWJncm91cCAxIiksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgIyAgIH0NCiAgIyB9DQoNCiNnZ3Bsb3RseShwKQ0KcA0KI2dyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KIyAxMiBDb2RlICYgVmVyc2lvbnMNCg0KUGlwZWxpbmV2ZXJzaW9uOiB2MSBiYXNlZCBvbjoNCjxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvd29ya2Zsb3dzL3ZpZ25ldHRlcy9HZW9NeFdvcmtmbG93cy9pbnN0L2RvYy9HZW9teFRvb2xzX1JOQS1OR1NfQW5hbHlzaXMuaHRtbD4NCg0KVGhlIHVuZGVybHlpbmcgY29kZSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIHRoZSAnQ29kZScsIGJ1dHRvbiBvbiB0aGUgdG9wDQpvZiB0aGlzIHBhZ2UuIENob29zZSBvcHRpb24gJ2Rvd25sb2FkIFJtZCcgdG8gZG93bmxvYWQgdGhlIGZ1bGwgcGlwZWxpbmUNCndoaWNoIGNhbiBiZSBvcGVuZWQgaW4gUiBvciBSc3R1ZGlvLiBTb21lIGZpbGVwYXRocyBhcmUgaGFyZGNvZGVkIGFuZA0KbmVlZCB0byBiZSBjaGFuZ2VkIGFjY29yZGluZyB0byB5b3VyIHNldHVwLg0KDQojIDEyLjEgUiBzZXNzaW9uIGluZm9ybWF0aW9uDQoNCmBgYHtyIHNlc3Npb25faW5mb30NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQojIyAxMi4yIFJlZmVyZW5jZXMNCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL2RlY29yYXRpb24tc3Ryb2tlLWZsYXQucG5nKQ0KDQpgYGB7cn0NCmtuaXRyOjprbml0X2V4aXQoKQ0KYGBgDQoNCiMgOS4wIHJlbG9hZCBkYXRhDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcHJlcGFyZX0NCiNyZWxvYWQgZGF0YQ0KIyBEYXRhIDwtDQojICAgcmVhZE5hbm9TdHJpbmdHZW9NeFNldChkY2NGaWxlcyA9IERDQ0ZpbGVzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGtjRmlsZXMgPSBQS0NGaWxlcywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YVNoZWV0ID0gIlNoZWV0MSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFEY2NDb2xOYW1lID0gIlNhbXBsZV9JRCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwcm90b2NvbERhdGFDb2xOYW1lcyA9IGMoImFvaSIsICJyb2kiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KIyANCiMgcGtjcyA8LSBhbm5vdGF0aW9uKERhdGEpDQojIG1vZHVsZXMgPC0gZ3N1YigiLnBrYyIsICIiLCBwa2NzKQ0KIyANCiMgI3NoaWZ0IGFueSBleHByZXNzaW9uIGNvdW50cyB3aXRoIGEgdmFsdWUgb2YgMCB0byAxIHRvIGVuYWJsZSBpbiBkb3duc3RyZWFtIHRyYW5zZm9ybWF0aW9ucy4NCiMgRGF0YSA8LSBzaGlmdENvdW50c09uZShEYXRhLCB1c2VEQUxvZ2ljID0gVFJVRSkNCiMgDQojICNjb2xsYXBzX3RhcmdldHMNCiMgdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQojIGRpbSh0YXJnZXRfRGF0YSkNCiMgDQojICNub3JtYWxpemUNCiMgdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwgZGF0YV90eXBlID0gIlJOQSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpcmVkUXVhbnRpbGUgPSAuNzUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9FbHQgPSAicV9ub3JtIikNCmBgYA0KDQojIDkuNCBSdW4gYWR2YW5jZWQgc3BhdGlhbCBkZXZvbmNvbHV0aW9uDQoNCmBgYHtyfQ0KIyB2ZWN0b3IgaWRlbnRpZnlpbmcgcHVyZSB0dW1vciBzZWdtZW50czoNCnRhcmdldF9EYXRhJGlzdHVtb3IgPSB0YXJnZXRfRGF0YSRBTk4yID09ICJFcGl0aGVsaXVtIg0KDQojIHJ1biBzcGF0aWFsZGVjb24gd2l0aCBhbGwgdGhlIGJlbGxzIGFuZCB3aGlzdGxlczoNCnJlc3RpbHMgPSBydW5zcGF0aWFsZGVjb24ob2JqZWN0ID0gdGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZWx0ID0gInFfbm9ybSIsICAgICAgICAgICAgICAgICAgICAjIG5vcm1hbGl6ZWQgZGF0YQ0KICAgICAgICAgICAgICAgICAgICAgICAgICByYXdfZWx0ID0gImV4cHJzIiwgICAgICAgICAgICAgICAgICAgICAgIyBleHBlY3RlZCBiYWNrZ3JvdW5kIGNvdW50cyBmb3IgZXZlcnkgZGF0YSBwb2ludCBpbiBub3JtDQogICAgICAgICAgICAgICAgICAgICAgICAgIFggPSBzYWZlVE1FLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNhZmVUTUUgbWF0cml4LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbG1lcmdlcyA9IHNhZmVUTUUubWF0Y2hlcywgICAgICAgICAgICMgc2FmZVRNRS5tYXRjaGVzIG9iamVjdCwgdXNlZCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfY291bnRzID0gdGFyZ2V0X0RhdGEkbnVjbGVpLCAgICAgICMgbnVjbGVpIGNvdW50cywgdXNlZCB0byBlc3RpbWF0ZSB0b3RhbCBjZWxscw0KICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wdXJlX3R1bW9yID0gdGFyZ2V0X0RhdGEkaXN0dW1vciwgICAjIGlkZW50aXRpZXMgb2YgdGhlIFR1bW9yIHNlZ21lbnRzL29ic2VydmF0aW9ucw0KICAgICAgICAgICAgICAgICAgICAgICAgICBuX3R1bW9yX2NsdXN0ZXJzID0gNSkgICAgICAgICAgICAgICAgICAgIyBob3cgbWFueSBkaXN0aW5jdCB0dW1vciBwcm9maWxlcyB0byBhcHBlbmQgdG8gc2FmZVRNRQ0KDQojc3RyKHBEYXRhKHJlc3RpbHMpKQ0KaGVhdG1hcChzd2VlcChyZXN0aWxzQGV4cGVyaW1lbnREYXRhQG90aGVyJFNwYXRpYWxEZWNvbk1hdHJpeCwgMSwgYXBwbHkocmVzdGlsc0BleHBlcmltZW50RGF0YUBvdGhlciRTcGF0aWFsRGVjb25NYXRyaXgsIDEsIG1heCksICIvIiksDQogICAgICAgICBsYWJSb3cgPSBOQSwgbWFyZ2lucyA9IGMoMTAsIDUpKQ0KDQpgYGANCg0KPCEtLSAjIDkuMy4yIFBsb3R0aW5nIGRlY29udm9sdXRpb24gcmVzdWx0cyAtLT4NCg0KPCEtLSBgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9N30gLS0+DQoNCjwhLS0gIyBGb3IgcmVmZXJlbmNlLCBzaG93IHRoZSBUSUxzIGNvbG9yIGRhdGEgb2JqZWN0IHVzZWQgYnkgdGhlIHBsb3R0aW5nIGZ1bmN0aW9ucyAgLS0+DQoNCjwhLS0gIyB3aGVuIHNhZmVUTUUgaGFzIGJlZW4gdXNlZDogLS0+DQoNCjwhLS0gZGF0YSgiY2VsbGNvbHMiKSAtLT4NCg0KPCEtLSAjY2VsbGNvbHMgLS0+DQoNCjwhLS0gbyA9IGhjbHVzdChkaXN0KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzKSkpJG9yZGVyIC0tPg0KDQo8IS0tIGxheW91dChtYXRyaXgoYygxLCAyKSwgMSksIHdpZHRocyA9IGMoNywgMykpIC0tPg0KDQo8IS0tIFRJTF9iYXJwbG90KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzWywgb10pLCAtLT4NCg0KPCEtLSAgICAgICAgICAgICBob3JpeiA9IFRSVUUsIGRyYXdfbGVnZW5kID0gVFJVRSwgY2V4Lm5hbWVzID0gMC45KSAtLT4NCg0KPCEtLSAjcGFyKG1hcj1jKDIsIDE1LCAyLCAyKSkgLS0+DQoNCjwhLS0gIyBvciB0aGUgcHJvcG9ydGlvbnMgb2YgY2VsbHM6IC0tPg0KDQo8IS0tIHRlbXAgPSByZXBsYWNlKHJlcyRwcm9wX29mX25vbnR1bW9yLCBpcy5uYShyZXMkcHJvcF9vZl9ub250dW1vciksIDApIC0tPg0KDQo8IS0tIG8gPSBoY2x1c3QoZGlzdCh0ZW1wW3JlcyRBTk4yID09ICJDRDQ1IixdKSkkb3JkZXIgLS0+DQoNCjwhLS0gVElMX2JhcnBsb3QodChyZXMkcHJvcF9vZl9hbGwpLCAgLS0+DQoNCjwhLS0gICAgICAgICAgICAgaG9yaXogPSBUUlVFLCBkcmF3X2xlZ2VuZCA9IFRSVUUsIGNleC5uYW1lcyA9IDAuOSkgLS0+DQoNCjwhLS0gYGBgIC0tPg0KDQojIDkuMy4zIEZsb3JldHMgb2YgU3BhdGlhbCBkZWNvbnZvbHV0aW9uDQoNClRoZSBzZWNvbmQgZnVuY3Rpb24gaXMgImZsb3JldHMiLCB1c2VkIGZvciBwbG90dGluZyBjZWxsIGFidW5kYW5jZXMgYXRvcA0Kc29tZSAyLUQgcHJvamVjdGlvbi4gSGVyZSwgd2UnbGwgcGxvdCBjZWxsIGFidW5kYW5jZXMgYXRvcCB0aGUgZmlyc3QgMg0KcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgdGhlIGRhdGE6DQoNCmBgYHtyfQ0KeHkgPC0gcmVhZF9leGNlbCgiTDovcGtsb29zdGVybWFuL0tpZG5leV9vcmdhbl9hdGxhcy9hbm5vdGF0aW9uL09yZ2FuQXRsYXNfS2lkbmV5Lnhsc3giLCBzaGVldCA9ICJTZWdtZW50UHJvcGVydGllcyIpDQp4eSRuZXd4IDwtICh4eSRST0lDb29yZGluYXRlWCAtIHh5JFNjYW5PZmZzZXRYKQ0KeHkkbmV3eSA8LSAoeHkkUk9JQ29vcmRpbmF0ZVkgLSB4eSRTY2FuT2Zmc2V0WSkNCnh5IDwtIHh5WyxjKCJTZWdtZW50RGlzcGxheU5hbWUiLCAibmV3eCIsICJuZXd5IildDQp4eVsiYW9pIl0gPC0geHlbIlNlZ21lbnREaXNwbGF5TmFtZSJdDQphbm5vdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YQ0KYW5ub3QgPC0gYW5ub3QgJT4lIGlubmVyX2pvaW4oeHksIGJ5ID0gJ2FvaScpICMlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCmFubm90JHggPC0gYW5ub3QkbmV3eA0KYW5ub3QkeSA8LSBhbm5vdCRuZXd5DQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1LCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjUwJSJ9DQojUDM2ID0gY3JlYXRlUGFsZXR0ZShsZW5ndGgocm93bmFtZXModChyZXMkYmV0YSkpKSwgIGMoIiNmZjAwMDAiLCAiIzAwZmYwMCIsICIjMDAwMGZmIikpDQoNCnhsaW0gPC0gbWF4KHJlc0BwaGVub0RhdGFAZGF0YSR4KQ0KeWxpbSA8LSBtYXgocmVzQHBoZW5vRGF0YUBkYXRhJHkpDQoNCmluZCA8LSBwRGF0YShyZXMpJHNsaWRlX25hbWUgPT0gImh1X2tpZG5leV8wMDEiDQpyZXMyIDwtIHJlc1ssaW5kXQ0KDQojYSA8LSByZXMyWyFyZXMyQHBoZW5vRGF0YUBkYXRhW1siYmV0YSJdXT09MF0NCg0KI3NsaWRlX2xpc3RbWzRdXSA8LSBmbGlwWShzbGlkZV9saXN0W1s0XV0pDQoNCmZsdW9yIDwtIGZsdW9yTGVnZW5kKG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIG5yb3cgPSA0LCB0ZXh0U2l6ZSA9IDUsIGFscGhhID0gMC4yNSkNCg0KaW0gPC0gcGxvdFNwYXRpYWxPdmVybGF5KG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIGxlZ2VuZCA9IEZBTFNFLCBoaVJlcyA9IEZBTFNFLCBjb3JuZXIgPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgICAgICAgICBzY2FsZUJhcldpZHRoID0gMC41LCB0ZXh0RGlzdGFuY2UgPSA1LCBzY2FsZUJhckNvbG9yID0gIndoaXRlIiwgaW1hZ2UgPSBUUlVFKSArIA0KICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gcGFzdGUoIldob2xlIHNsaWRlIE9NRS1USUZGIiwgc2xpZGVOYW1lKHNsaWRlX2xpc3RbWzRdXSkpKQ0KDQojcGxvdCgwLCB4bGltID0gYygwLCB4bGltKzQwMDAwKSwgeWxpbSA9IGMoMCwgeWxpbSksIGF4ZXMgPSBGQUxTRSkNCg0KY293cGxvdDo6Z2dkcmF3KCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoaW0sIHggPSAtMC4zLCB5ID0gMCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoZmx1b3IsIHNjYWxlID0gMC4xMiwgeCA9IDAuMSwgeSA9IDApICMrICMsIHNjYWxlID0gMC4xMiwgeCA9IC0wLjMsIHkgPSAtMC4yNSkNCg0KDQpmbG9yZXRzKA0KICB4ID0gcmVzMkBwaGVub0RhdGFAZGF0YSR4LCMgKyA0MDAwMCwNCiAgeSA9IHJlczJAcGhlbm9EYXRhQGRhdGEkeSwNCiAgY29sID0gUDM2LA0KICAjY29sID0gMTpsZW5ndGgocm93bmFtZXModChhJGJldGEpKSksDQogIHhsaW0gPSBjKDAseGxpbSs0MDAwMCksDQogIHlsaW0gPSBjKDAseWxpbSksDQogIGIgPSB0KGEkYmV0YSksDQogIHJlc2NhbGUuYnkuc3FydCA9IFRSVUUsDQogIGFkZCA9IEZBTFNFLA0KICBjZXggPSAxLCB4bGFiID0gIiIsIHlsYWIgPSAiIikNCg0KbGVnZW5kKCJyaWdodCIsDQogICAgICAgI2ZpbGwgPSAxOmxlbmd0aChyb3duYW1lcyh0KGEkYmV0YSkpKSwNCiAgICAgICBmaWxsID0gUDM2LA0KICAgICAgIGxlZ2VuZCA9IHJvd25hbWVzKHQoYSRiZXRhKSksIGNleCA9IDEuNSkNCmBgYA0KDQojIDkuNCBjb21iaW5pbmcgY2VsbHR5cGVzDQoNCldoZW4gdHdvIGNlbGwgdHlwZXMgYXJlIHRvbyBzaW1pbGFyLCB0aGUgZXN0aW1hdGlvbiBvZiB0aGVpciBhYnVuZGFuY2VzDQpiZWNvbWVzIHVuc3RhYmxlLiBIb3dldmVyLCB0aGVpciBzdW0gY2FuIHN0aWxsIGJlIGVzdGltYXRlZCBlYXNpbHkuIFRoZQ0KZnVuY3Rpb24gImNvbGxhcHNlQ2VsbFR5cGVzIiB0YWtlcyBhIGRlY29udm9sdXRpb24gcmVzdWx0cyBvYmplY3QgYW5kDQpjb2xsYXBzZXMgYW55IGNvbHNlbHktcmVsYXRlZCBjZWxsIHR5cGVzIHlvdSB0ZWxsIGl0IHRvDQoNCmBgYHtyfQ0KbWF0Y2hpbmcgPSBsaXN0KCkNCm1hdGNoaW5nJG15ZWxvaWQgPSBjKCAibWFjcm9waGFnZXMiLCAibW9ub2N5dGVzIiwgIm1EQ3MiKQ0KbWF0Y2hpbmckVC5OSyA9IGMoIkNENC5ULmNlbGxzIiwiQ0Q4LlQuY2VsbHMiLCAiVHJlZyIsICJOSyIpDQptYXRjaGluZyRCID0gYygiQiIpDQptYXRjaGluZyRtYXN0ID0gYygibWFzdCIpDQptYXRjaGluZyRuZXV0cm9waGlscyA9IGMoIm5ldXRyb3BoaWxzIikNCm1hdGNoaW5nJHN0cm9tYSA9IGMoImVuZG90aGVsaWFsLmNlbGxzIiwgImZpYnJvYmxhc3RzIikNCg0KY29sbGFwc2VkID0gY29sbGFwc2VDZWxsVHlwZXMoZml0ID0gcmVzdGlscywgbWF0Y2hpbmcgPSBtYXRjaGluZykNCg0KaGVhdG1hcChjb2xsYXBzZWQkYmV0YSwgY2V4Um93ID0gMC44NSwgY2V4Q29sID0gMC43NSkNCmBgYA0KDQojIExpZ2FuZC1yZWNlcHRvciBhbmFseXNlDQoNCmBgYHtyfQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic2FleXNsYWIvbmljaGVuZXRyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCdjaXJjbGl6ZScpDQpsaWJyYXJ5KG5pY2hlbmV0cikNCmxpYnJhcnkoU2V1cmF0KQ0KbGlicmFyeShjaXJjbGl6ZSkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIEdldCBjb3VudHMgZGF0YQ0KY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKQ0KY291bnRzJFggPC0gcm93bmFtZXMoY291bnRzKQ0KI3Jvd25hbWVzKGNvdW50cykgPC0gTlVMTA0KI2NvdW50cyA8LSBhcy5tYXRyaXgoY291bnRzKQ0KDQojIEdldCBtZXRhIGRhdGENCm1ldGEgPC0gcERhdGEodGFyZ2V0X0RhdGEpDQptZXRhIDwtIG1ldGFbYygiQU5OMSIsICJBTk4yIiwgInNsaWRlX25hbWUiKV0NCm1ldGEkU2FtcGxlSUQgPC0gcHJvdG9jb2xEYXRhKHRhcmdldF9EYXRhKVtbIlNhbXBsZUlEIl1dDQptZXRhJHJvaSA8LSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpW1sicm9pIl1dDQoNCiMgY3JlYXRlIFNldXJhdCBmb3IgYmV0dGVyIG5pY2hlIGNvb3BlcmF0aW9uDQpTZXVyYXQuRGF0YSA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzLCBtZXRhLmRhdGEgPSBtZXRhKSANCg0KIyMgQ2hlY2sgbWV0YSBkYXRhDQojU2V1cmF0LkRhdGFAbWV0YS5kYXRhICU+JSBoZWFkKCkNCiNTZXVyYXQuRGF0YUBtZXRhLmRhdGEkUmVnaW9uICU+JSB0YWJsZSgpIA0KDQojIyBDaGFuZ2UgaWRlbnRzIHRvIGNvcnJlY3QgYW5ub3RhdGlvbg0KU2V1cmF0LkRhdGEgPC0gU2V0SWRlbnQoU2V1cmF0LkRhdGEsIHZhbHVlID0gIkFOTjEiKQ0KI1NldXJhdC5EYXRhQGFjdGl2ZS5pZGVudA0KDQojIyMjIyMjIFJlYWQgaW4gbGlnYW5kLXRhcmdldCBwcmlvciBtb2RlbCwgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgYW5kIHdlaWdodGVkIGludGVncmF0ZWQgbmV0d29ya3MNCmxpZ2FuZF90YXJnZXRfbWF0cml4ID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy9saWdhbmRfdGFyZ2V0X21hdHJpeC5yZHMiKSkNCiNsaWdhbmRfdGFyZ2V0X21hdHJpeFsxOjUsMTo1XSAjIHRhcmdldCBnZW5lcyBpbiByb3dzLCBsaWdhbmRzIGluIGNvbHVtbnMNCg0KbHJfbmV0d29yayA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvbHJfbmV0d29yay5yZHMiKSkNCiNoZWFkKGxyX25ldHdvcmspDQoNCndlaWdodGVkX25ldHdvcmtzID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy93ZWlnaHRlZF9uZXR3b3Jrcy5yZHMiKSkNCndlaWdodGVkX25ldHdvcmtzX2xyID0gd2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnICU+JSBpbm5lcl9qb2luKGxyX25ldHdvcmsgJT4lIGRpc3RpbmN0KGZyb20sdG8pLCBieSA9IGMoImZyb20iLCJ0byIpKQ0KI2hlYWQod2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnKSAjIGludGVyYWN0aW9ucyBhbmQgdGhlaXIgd2VpZ2h0cyBpbiB0aGUgbGlnYW5kLXJlY2VwdG9yICsgc2lnbmFsaW5nIG5ldHdvcmsNCg0KDQojIyMjIERlZmluZSBhIHNlbmRlciBhbmQgcmVjZWl2ZXIgcG9wdWxhdGlvbiBhbmQgZGV0ZXJtaW5lIHdoaWNoIGdlbmVzIGFyZSBleHByZXNzZWQgaW4gYm90aCBwb3B1bGF0aW9ucw0KDQojIyByZWNlaXZlcg0KcmVjZWl2ZXIgPSBjKCJDRDEwKyIpDQpleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIgPSBnZXRfZXhwcmVzc2VkX2dlbmVzKHJlY2VpdmVyLCBTZXVyYXQuRGF0YSwgcGN0ID0gMC4xMCkNCmJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gZXhwcmVzc2VkX2dlbmVzX3JlY2VpdmVyICU+JSAuWy4gJWluJSByb3duYW1lcyhsaWdhbmRfdGFyZ2V0X21hdHJpeCldDQoNCiMjIHNlbmRlcg0Kc2VuZGVyX2NlbGx0eXBlcyA9IGMoIlBhbkNLKyIpDQpsaXN0X2V4cHJlc3NlZF9nZW5lc19zZW5kZXIgPSBzZW5kZXJfY2VsbHR5cGVzICU+JSB1bmlxdWUoKSAlPiUgbGFwcGx5KGdldF9leHByZXNzZWRfZ2VuZXMsIFNldXJhdC5EYXRhLCAwLjEwKSAjIGxhcHBseSB0byBnZXQgdGhlIGV4cHJlc3NlZCBnZW5lcyBvZiBldmVyeSBzZW5kZXIgY2VsbCB0eXBlIHNlcGFyYXRlbHkgaGVyZQ0KZXhwcmVzc2VkX2dlbmVzX3NlbmRlciA9IGxpc3RfZXhwcmVzc2VkX2dlbmVzX3NlbmRlciAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpDQoNCiMjIyMgSW1wb3J0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiByZWNpZXZlciBwb3B1bGF0aW9ucw0KZ2VuZXNldC5vaSA8LSB0b3BfZw0KDQojIyMjIyAgRGVmaW5lIGEgc2V0IG9mIGxpZ2FuZHMgaW4gc2VuZGVyIHBvcHVsYXRpb25zDQpsaWdhbmRzID0gbHJfbmV0d29yayAlPiUgcHVsbChmcm9tKSAlPiUgdW5pcXVlKCkNCnJlY2VwdG9ycyA9IGxyX25ldHdvcmsgJT4lIHB1bGwodG8pICU+JSB1bmlxdWUoKQ0KDQpleHByZXNzZWRfbGlnYW5kcyA9IGludGVyc2VjdChsaWdhbmRzLGV4cHJlc3NlZF9nZW5lc19zZW5kZXIpDQpleHByZXNzZWRfcmVjZXB0b3JzID0gaW50ZXJzZWN0KHJlY2VwdG9ycyxleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIpDQoNCnBvdGVudGlhbF9saWdhbmRzID0gbHJfbmV0d29yayAlPiUgZmlsdGVyKGZyb20gJWluJSBleHByZXNzZWRfbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIHB1bGwoZnJvbSkgJT4lIHVuaXF1ZSgpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIFBlcmZvcm0gTmljaGVuZXQgYW5hbHlzaXMNCg0KDQpsaWdhbmRfYWN0aXZpdGllcyA9IHByZWRpY3RfbGlnYW5kX2FjdGl2aXRpZXMoZ2VuZXNldCA9IGdlbmVzZXQub2ksIGJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gYmFja2dyb3VuZF9leHByZXNzZWRfZ2VuZXMsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIHBvdGVudGlhbF9saWdhbmRzID0gcG90ZW50aWFsX2xpZ2FuZHMpDQoNCmxpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pIA0KI2xpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pICU+JSBtdXRhdGUocmFuayA9IHJhbmsoZGVzYyhwZWFyc29uKSkpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMgSGlzdG9ncmFtIG9mIGxpZ2FuZCBhY3Rpdml0aWVzDQoNCnBfaGlzdF9saWdfYWN0aXZpdHkgPSBnZ3Bsb3QobGlnYW5kX2FjdGl2aXRpZXMsIGFlcyh4PXBlYXJzb24pKSArIA0KICBnZW9tX2hpc3RvZ3JhbShjb2xvcj0iYmxhY2siLCBmaWxsPSJkYXJrb3JhbmdlIikgICsgDQogICMgZ2VvbV9kZW5zaXR5KGFscGhhPS4xLCBmaWxsPSJvcmFuZ2UiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWluKGxpZ2FuZF9hY3Rpdml0aWVzICU+JSB0b3BfbigyMCwgcGVhcnNvbikgJT4lIHB1bGwocGVhcnNvbikpKSwgY29sb3I9InJlZCIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpICsgDQogIGxhYnMoeD0ibGlnYW5kIGFjdGl2aXR5IChQQ0MpIiwgeSA9ICIjIGxpZ2FuZHMiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KcF9oaXN0X2xpZ19hY3Rpdml0eQ0KDQoNCiMjIyBEb3QgYmxvdCBvZiB0b3AgbGlnYW5kcyBzcGxpdCBieSByZWdpb24NCg0KYmVzdF91cHN0cmVhbV9saWdhbmRzID0gYyhsaWdhbmRfYWN0aXZpdGllcyAlPiUgdG9wX24oMjAsIHBlYXJzb24pICU+JSBhcnJhbmdlKC1wZWFyc29uKSAlPiUgcHVsbCh0ZXN0X2xpZ2FuZCkgJT4lIHVuaXF1ZSgpKQ0KDQojIHRlc3QgPC0gU2V1cmF0LkRhdGENCiMgeWVldCA8LSBjKCJXTlQzQSIpDQojIGRhdGEoInBibWNfc21hbGwiKQ0KIyB0ZXN0W1siUk5BIl1dQHZhci5mZWF0dXJlcyA8LSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMNCiMgI2RhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXModGVzdFtbIlJOQSJdXSkpDQojIA0KIyBEb3RQbG90KHRlc3QsIGZlYXR1cmVzID0gYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xzID0gIlJkWWxCdSIpICsgUm90YXRlZEF4aXMoKQ0KDQojIyBlbHNlDQoNCmFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmID0gYmVzdF91cHN0cmVhbV9saWdhbmRzICU+JSBsYXBwbHkoZ2V0X3dlaWdodGVkX2xpZ2FuZF90YXJnZXRfbGlua3MsZ2VuZXNldCA9IGdlbmVzZXQub2ksIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIG4gPSAyNTApICU+JSBiaW5kX3Jvd3MoKQ0KYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3MgPSBwcmVwYXJlX2xpZ2FuZF90YXJnZXRfdmlzdWFsaXphdGlvbihsaWdhbmRfdGFyZ2V0X2RmID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIGN1dG9mZiA9IDAuMjUpDQoNCm9yZGVyX2xpZ2FuZHMgPSBpbnRlcnNlY3QoYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xuYW1lcyhhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rcykpICU+JSByZXYoKQ0Kb3JkZXJfdGFyZ2V0cyA9IGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmJHRhcmdldCAlPiUgdW5pcXVlKCkNCnZpc19saWdhbmRfdGFyZ2V0ID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3Nbb3JkZXJfdGFyZ2V0cyxvcmRlcl9saWdhbmRzXSAlPiUgdCgpDQoNCnBfbGlnYW5kX3RhcmdldF9uZXR3b3JrID0gdmlzX2xpZ2FuZF90YXJnZXQgJT4lIG1ha2VfaGVhdG1hcF9nZ3Bsb3QoIlByaW9yaXRpemVkIFBhbkNLKyBsaWdhbmRzIiwiQ0QxMCsgcG90ZW50aWFsIHJlY2VwdG9yIGNlbGxzIiwgY29sb3IgPSAicHVycGxlIixsZWdlbmRfcG9zaXRpb24gPSAidG9wIiwgeF9heGlzX3Bvc2l0aW9uID0gInRvcCIsbGVnZW5kX3RpdGxlID0gIlJlZ3VsYXRvcnkgcG90ZW50aWFsIikgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGVzbW9rZSIsICBoaWdoID0gInB1cnBsZSIsIGJyZWFrcyA9IGMoMCwwLjAwNSwwLjAxKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKQ0KDQpwX2xpZ2FuZF90YXJnZXRfbmV0d29yaw0KDQojIyBjaXJjb3MNCg0KIyBRQyBvbiBxdWFudGlsZQ0KY3V0b2ZmX2luY2x1ZGVfYWxsX2xpZ2FuZHMgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiR3ZWlnaHQgJT4lIHF1YW50aWxlKDAuNjYpDQphY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZl9jaXJjb3MgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiAlPiUgZmlsdGVyKHdlaWdodCA+IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzKQ0KDQpsaWdhbmRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkbGlnYW5kICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJGxpZ2FuZCAlPiUgdW5pcXVlKCkpDQp0YXJnZXRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkdGFyZ2V0ICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJHRhcmdldCAlPiUgdW5pcXVlKCkpDQogIA0KY2lyY29zX2xpbmtzID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYgJT4lIGZpbHRlcighdGFyZ2V0ICVpbiUgdGFyZ2V0c190b19yZW1vdmUgJiFsaWdhbmQgJWluJSBsaWdhbmRzX3RvX3JlbW92ZSkNCg0KIyBDb2xvcg0KZ3JpZF9jb2xfbGlnYW5kID1jKCJQYW5DSysiID0gInJlZCINCiAgICAgICAgICAgICAgICAgICMsIkdlbmVyYWwiID0gImdyZWVuIg0KICAgICAgICAgICAgICAgICAgICkNCmdyaWRfY29sX3RhcmdldCA9YygiQ0QxMCsiID0gImdyZWVuIikNCg0KZ3JpZF9jb2xfdGJsX2xpZ2FuZCA9IHRpYmJsZShsaWdhbmRfdHlwZSA9IGdyaWRfY29sX2xpZ2FuZCAlPiUgbmFtZXMoKSwgY29sb3JfbGlnYW5kX3R5cGUgPSBncmlkX2NvbF9saWdhbmQpDQpncmlkX2NvbF90YmxfdGFyZ2V0ID0gdGliYmxlKHRhcmdldF90eXBlID0gZ3JpZF9jb2xfdGFyZ2V0ICU+JSBuYW1lcygpLCBjb2xvcl90YXJnZXRfdHlwZSA9IGdyaWRfY29sX3RhcmdldCkNCg0KY2lyY29zX2xpbmtzID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUobGlnYW5kID0gcGFzdGUobGlnYW5kLCIgIikpICMgZXh0cmEgc3BhY2U6IG1ha2UgYSBkaWZmZXJlbmNlIGJldHdlZW4gYSBnZW5lIGFzIGxpZ2FuZCBhbmQgYSBnZW5lIGFzIHRhcmdldCENCmNpcmNvc19saW5rcyA9IGNpcmNvc19saW5rcyAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfbGlnYW5kLCBieSA9IGNoYXJhY3RlcigpKSAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfdGFyZ2V0LCBieSA9IGNoYXJhY3RlcigpKQ0KbGlua3NfY2lyY2xlID0gY2lyY29zX2xpbmtzICU+JSBzZWxlY3QobGlnYW5kLHRhcmdldCwgd2VpZ2h0KQ0KDQpsaWdhbmRfY29sb3IgPSBjaXJjb3NfbGlua3MgJT4lIGRpc3RpbmN0KGxpZ2FuZCxjb2xvcl9saWdhbmRfdHlwZSkNCmdyaWRfbGlnYW5kX2NvbG9yID0gbGlnYW5kX2NvbG9yJGNvbG9yX2xpZ2FuZF90eXBlICU+JSBzZXRfbmFtZXMobGlnYW5kX2NvbG9yJGxpZ2FuZCkNCnRhcmdldF9jb2xvciA9IGNpcmNvc19saW5rcyAlPiUgZGlzdGluY3QodGFyZ2V0LGNvbG9yX3RhcmdldF90eXBlKQ0KZ3JpZF90YXJnZXRfY29sb3IgPSB0YXJnZXRfY29sb3IkY29sb3JfdGFyZ2V0X3R5cGUgJT4lIHNldF9uYW1lcyh0YXJnZXRfY29sb3IkdGFyZ2V0KQ0KDQpncmlkX2NvbCA9IGMoZ3JpZF9saWdhbmRfY29sb3IsZ3JpZF90YXJnZXRfY29sb3IpDQoNCiMgZ2l2ZSB0aGUgb3B0aW9uIHRoYXQgbGlua3MgaW4gdGhlIGNpcmNvcyBwbG90IHdpbGwgYmUgdHJhbnNwYXJhbnQgfiBsaWdhbmQtdGFyZ2V0IHBvdGVudGlhbCBzY29yZQ0KdHJhbnNwYXJlbmN5ID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUod2VpZ2h0ID0od2VpZ2h0LW1pbih3ZWlnaHQpKS8obWF4KHdlaWdodCktbWluKHdlaWdodCkpKSAlPiUgbXV0YXRlKHRyYW5zcGFyZW5jeSA9IDEtd2VpZ2h0KSAlPiUgLiR0cmFuc3BhcmVuY3kgDQoNCiMgU3BlY2lmaWMgb3JkZXIgd2l0aCBnYXBzIGlmIG5lZWRlZA0KdGFyZ2V0X29yZGVyID0gY2lyY29zX2xpbmtzJHRhcmdldCAlPiUgdW5pcXVlKCkNCiNsaWdhbmRfb3JkZXIgPSBjKENBRl9zcGVjaWZpY19saWdhbmRzLGdlbmVyYWxfbGlnYW5kcyxlbmRvdGhlbGlhbF9zcGVjaWZpY19saWdhbmRzKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCmxpZ2FuZF9vcmRlciA9IGMoKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCm9yZGVyID0gYyhsaWdhbmRfb3JkZXIsdGFyZ2V0X29yZGVyKQ0KDQp3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSA9IDAuNQ0Kd2lkdGhfZGlmZmVyZW50X2NlbGwgPSA2DQp3aWR0aF9saWdhbmRfdGFyZ2V0ID0gMTUNCndpZHRoX3NhbWVfY2VsbF9zYW1lX3RhcmdldF90eXBlID0gMC41DQoNCmdhcHMgPSBjKA0KICAjIHdpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIlBhbkNLKyIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgd2lkdGhfZGlmZmVyZW50X2NlbGwsDQogICMgcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcihsaWdhbmRfdHlwZSA9PSAiR2VuZXJhbCIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgIyB3aWR0aF9kaWZmZXJlbnRfY2VsbCwNCiAgI3JlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIkVuZG90aGVsaWFsLXNwZWNpZmljIikgJT4lIGRpc3RpbmN0KGxpZ2FuZCkgJT4lIG5yb3coKSAtMSkpLCANCiAgI3dpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV90YXJnZXRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIodGFyZ2V0X3R5cGUgPT0gIkNEMTArIikgJT4lIGRpc3RpbmN0KHRhcmdldCkgJT4lIG5yb3coKSAtMSkpLA0KICB3aWR0aF9saWdhbmRfdGFyZ2V0DQogICkNCg0KI2NpcmNvcy5wYXIoZ2FwLmRlZ3JlZSA9IGdhcHMpDQoNCmNpcmNvcy5wYXIoUkVTRVQgPSBUUlVFKQ0KDQpjaG9yZERpYWdyYW0obGlua3NfY2lyY2xlLCANCiAgICAgICAgICAgICBkaXJlY3Rpb25hbCA9IDEsDQogICAgICAgICAgICAgI29yZGVyPW9yZGVyLA0KICAgICAgICAgICAgIGxpbmsuc29ydCA9IFRSVUUsIA0KICAgICAgICAgICAgIGxpbmsuZGVjcmVhc2luZyA9IEZBTFNFLCANCiAgICAgICAgICAgICBncmlkLmNvbCA9IGdyaWRfY29sLA0KICAgICAgICAgICAgIHRyYW5zcGFyZW5jeSA9IHRyYW5zcGFyZW5jeSwgDQogICAgICAgICAgICAgZGlmZkhlaWdodCA9IDAuMDA1LCANCiAgICAgICAgICAgICBkaXJlY3Rpb24udHlwZSA9IGMoImRpZmZIZWlnaHQiLCAiYXJyb3dzIiksDQogICAgICAgICAgICAgbGluay5hcnIudHlwZSA9ICJiaWcuYXJyb3ciLCANCiAgICAgICAgICAgICBsaW5rLnZpc2libGUgPSBsaW5rc19jaXJjbGUkd2VpZ2h0ID49IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzLA0KICAgICAgICAgICAgIGFubm90YXRpb25UcmFjayA9ICJncmlkIiwNCiAgICAgICAgICAgICBwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gMC4wNzUpKQ0KICAgICAgICAgICAgICNwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gbWF4KHN0cndpZHRoKHVubGlzdChkaW1uYW1lcyhsaW5rc19jaXJjbGUpKSkpKSkgIA0KICAgICAgICAgICAgIA0KIyB3ZSBnbyBiYWNrIHRvIHRoZSBmaXJzdCB0cmFjayBhbmQgY3VzdG9taXplIHNlY3RvciBsYWJlbHMNCmNpcmNvcy50cmFjayh0cmFjay5pbmRleCA9IDEsIHBhbmVsLmZ1biA9IGZ1bmN0aW9uKHgsIHkpIHsNCiAgICBjaXJjb3MudGV4dChDRUxMX01FVEEkeGNlbnRlciwgQ0VMTF9NRVRBJHlsaW1bMV0sIENFTExfTUVUQSRzZWN0b3IuaW5kZXgsDQogICAgICAgIGZhY2luZyA9ICJjbG9ja3dpc2UiLCBuaWNlRmFjaW5nID0gVFJVRSwgYWRqID0gYygwLCAwLjU1KSwgY2V4ID0gMSkNCn0sIGJnLmJvcmRlciA9IE5BKSANCg0KbGVnZW5kKCJib3R0b21sZWZ0IiwgDQogICAgICAgdGl0bGUgPSAiQ2VsbCBzcGVjaWZpYyIsDQogICAgICAgY2V4PSAzLCANCiAgICAgICBsZWdlbmQgPSB1bmlxdWUoYyhjaXJjb3NfbGlua3MkbGlnYW5kX3R5cGUsIHVuaXF1ZShjaXJjb3NfbGlua3MkdGFyZ2V0X3R5cGUpKSksDQogICAgICAgZmlsbCA9IHVuaXF1ZShjKGdyaWRfY29sKSkpDQp0aXRsZSgiTGlnYW5kLXJlY2VwdG9yIGNvbm5lY3Rpb25zIikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTB9DQojIGdldCB0aGUgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgb2YgdGhlIHRvcC1yYW5rZWQgbGlnYW5kcw0KbHJfbmV0d29ya190b3AgPSBscl9uZXR3b3JrICU+JSBmaWx0ZXIoZnJvbSAlaW4lIGJlc3RfdXBzdHJlYW1fbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIGRpc3RpbmN0KGZyb20sdG8pDQpiZXN0X3Vwc3RyZWFtX3JlY2VwdG9ycyA9IGxyX25ldHdvcmtfdG9wICU+JSBwdWxsKHRvKSAlPiUgdW5pcXVlKCkNCg0KIyAjIGdldCB0aGUgd2VpZ2h0cyBvZiB0aGUgbGlnYW5kLXJlY2VwdG9yIGludGVyYWN0aW9ucyBhcyB1c2VkIGluIHRoZSBOaWNoZU5ldCBtb2RlbA0KIyB3ZWlnaHRlZF9uZXR3b3JrcyA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvd2VpZ2h0ZWRfbmV0d29ya3MucmRzIikpDQpscl9uZXR3b3JrX3RvcF9kZiA9IHdlaWdodGVkX25ldHdvcmtzJGxyX3NpZyAlPiUgZmlsdGVyKGZyb20gJWluJSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMgJiB0byAlaW4lIGJlc3RfdXBzdHJlYW1fcmVjZXB0b3JzKQ0KDQojIGNvbnZlcnQgdG8gYSBtYXRyaXgNCmxyX25ldHdvcmtfdG9wX2RmID0gbHJfbmV0d29ya190b3BfZGYgJT4lIHNwcmVhZCgiZnJvbSIsIndlaWdodCIsZmlsbCA9IDApDQpscl9uZXR3b3JrX3RvcF9tYXRyaXggPSBscl9uZXR3b3JrX3RvcF9kZiAlPiUgc2VsZWN0KC10bykgJT4lIGFzLm1hdHJpeCgpICU+JSBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKGxyX25ldHdvcmtfdG9wX2RmJHRvKQ0KDQojIHBlcmZvcm0gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gb3JkZXIgdGhlIGxpZ2FuZHMgYW5kIHJlY2VwdG9ycw0KZGlzdF9yZWNlcHRvcnMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCwgbWV0aG9kID0gImJpbmFyeSIpDQpoY2x1c3RfcmVjZXB0b3JzID0gaGNsdXN0KGRpc3RfcmVjZXB0b3JzLCBtZXRob2QgPSAid2FyZC5EMiIpDQpvcmRlcl9yZWNlcHRvcnMgPSBoY2x1c3RfcmVjZXB0b3JzJGxhYmVsc1toY2x1c3RfcmVjZXB0b3JzJG9yZGVyXQ0KDQpkaXN0X2xpZ2FuZHMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCAlPiUgdCgpLCBtZXRob2QgPSAiYmluYXJ5IikNCmhjbHVzdF9saWdhbmRzID0gaGNsdXN0KGRpc3RfbGlnYW5kcywgbWV0aG9kID0gIndhcmQuRDIiKQ0Kb3JkZXJfbGlnYW5kc19yZWNlcHRvciA9IGhjbHVzdF9saWdhbmRzJGxhYmVsc1toY2x1c3RfbGlnYW5kcyRvcmRlcl0NCg0KDQp2aXNfbGlnYW5kX3JlY2VwdG9yX25ldHdvcmsgPSBscl9uZXR3b3JrX3RvcF9tYXRyaXhbb3JkZXJfcmVjZXB0b3JzLCBvcmRlcl9saWdhbmRzX3JlY2VwdG9yXQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yayA9IHZpc19saWdhbmRfcmVjZXB0b3JfbmV0d29yayAlPiUgdCgpICU+JSBtYWtlX2hlYXRtYXBfZ2dwbG90KCJQcmlvcml0aXplZCBQYW5DSytsaWdhbmRzIiwiUmVjZXB0b3JzIGV4cHJlc3NlZCBieSBDRDEwKyIsIGNvbG9yID0gIm1lZGl1bXZpb2xldHJlZCIsIHhfYXhpc19wb3NpdGlvbiA9ICJ0b3AiLGxlZ2VuZF90aXRsZSA9ICJQcmlvciBpbnRlcmFjdGlvbiBwb3RlbnRpYWwiKQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yaw0KYGBgDQoNCiMgQ05WDQoNCmBgYHtyfQ0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJpbmZlcmNudiIpDQpsaWJyYXJ5KGluZmVyY252KQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWVyaWNrc28vU3BhdGlhbEluZmVyQ05WIikNCiNsaWJyYXJ5KFNwYXRpYWxJbmZlckNOVikNCmBgYA0KDQpgYGB7ciBjcmVhdGUgY252IGZpbGVzX30NCmZpbGUgPC0gcmVhZExpbmVzKFBLQ0ZpbGVzKQ0KZmlsZSA8LSBnc3ViKCcgJywgJycsDQogICAgICAgIGdzdWIoJyInLCAnJywgZmlsZSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBhbmQgcmVtb3ZlIHNwYWNlDQoNCmRpc3BsYXkgPC0gZmlsZVtncmVwbCgiRGlzcGxheU5hbWUiLCBmaWxlLCBmaXhlZCA9IFRSVUUpXSAgICAgICAgICMgR3JhYiBkaXNwbGF5IG5hbWVzDQpkaXNwbGF5IDwtIGdzdWIoJ0Rpc3BsYXlOYW1lJywgIiIsDQogICAgICAgICAgIGdzdWIoJyInLCAiIiwNCiAgICAgICAgICAgZ3N1YignOicsICIiLA0KICAgICAgICAgICBnc3ViKCcsJywgIiIsDQogICAgICAgICAgIGdzdWIoJyAnLCAiIiwNCiAgICAgICAgICAgZ3N1YignXzAxJywgIiIsIGRpc3BsYXkpKSkpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JhYiBvbmx5IHRoZSBuYW1lcw0KZGlzcGxheSA8LSB1bmlxdWUoZGlzcGxheSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgZHVwbGljYXRlcw0KDQpjaHIgPC0gZmlsZVtncmVwKCJHZW5vbWVDb29yZGluYXRlcyIsIGZpbGUpKzFdICAgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgY2hyIHBvc2l0aW9ucyB1bmRlciB0aGUgR2Vub21lQ29vcmRpbmF0ZXMgbGluZQ0KY2hyIDwtZ3N1YignLCcsICIiLCBjaHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgdW53YW50ZWQgc3ltYm9scw0KDQpwb3NpdGlvbnMgPC0gZGF0YS5mcmFtZSgNCiAgbmFtZSA9IGRpc3BsYXksIA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKSANCnBvc2l0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHBvc2l0aW9ucykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3dhcCB0eXBlcyB0byByZWNvZ25pemUgc3BsaXQgY29sdW1ucyBhcyBzaW5ndWxhciBjb2x1bW5zDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIE5VTEwNCnJvd25hbWVzKHBvc2l0aW9ucykgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZXJDTlYgcmVxdWlyZXMgbm8gY29sdW1uIGFuZCByb3cgbmFtZXMNCg0Kd3JpdGUudGFibGUocG9zaXRpb25zLCAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsIA0KICAgICAgICAgICAgc2VwID0gIlx0IiwNCiAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsIA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIA0KICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXcml0ZSBhd2F5DQoNCmNvdW50cyA8LSB0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXQ0Kd3JpdGUudGFibGUoY291bnRzLCAiQ291bnRzLnRzdiIsIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KYW5ub3RhdGlvbiA8LSB0YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YQ0KYW5ub3RhdGlvbiRTYW1wbGVJRCA8LSByb3duYW1lcyhhbm5vdGF0aW9uKQ0KYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgIkFOTjIiKV0gICAgICAgICAgICAgICAgICAjIFNlbGVjdCBTYW1wbGVJRCBhbmQgdGhlIG5lZWRlZCBhbm5vdGF0aW9uIChDTlYgcmVxdWlyZXMgU2FtcGxlSUQgYW5kIG9ubHkgMSBhbm5vdGF0aW9uKQ0Kcm93bmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTA0KY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQp3cml0ZS50YWJsZShhbm5vdGF0aW9uLCAiQW5ub3RhdGlvbnMudHN2IiwgDQogICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSwgDQogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSBhbm5vdGF0aW9uIGZpbGUNCmBgYA0KDQoNCmBgYHtyIHJ1biBDTlZfLCBpbmNsdWRlPUZBTFNFfQ0KIyBQaWNrIG5vcm1hbC9yZWZlcmVuY2UgZ3JvdXAocykNCnJlZmVyZW5jZSA8LSBjKCJDb3J0aWNhbCBnbG9tZXJ1bHVzIikNCg0KIyBOYW1lIG91dHB1dCBmb2xkZXINCm91dF9kaXIgPC0gIkNOViINCg0KIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KaW5mZXJjbnZfb2JqID0gQ3JlYXRlSW5mZXJjbnZPYmplY3QocmF3X2NvdW50c19tYXRyaXg9ICJDb3VudHMudHN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9ICJBbm5vdGF0aW9ucy50c3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfb3JkZXJfZmlsZT0gImdlbmVfb3JkZXJfSHNfV1RBX3YxX3BrYy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2dlbmVfb3JkZXJfZmlsZT0gImdlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hyX2V4Y2x1ZGUgPSBjKCJjaHJNIikpICMgRGVmYXVsdCBleGNsdWRlcyBjaHJYLCBjaHJZIGFuZCBjaHJNLiBCeSBvbmx5IHBpY2tpbmcgY2hyTSB5b3UgaW5jbHVkZSB0aGUgWCBhbmQgWSBjaHJvbW9zb21lcy4gICAgIA0KDQojIHBlcmZvcm0gaW5mZXJjbnYgb3BlcmF0aW9ucyB0byByZXZlYWwgY252IHNpZ25hbC4gRm9yIGFsbCBvcHRpb25zOiBodHRwczovL3JkcnIuaW8vZ2l0aHViL2Jyb2FkaW5zdGl0dXRlL2luZmVyY252L21hbi9ydW4uaHRtbA0KaW5mZXJjbnZfb2JqID0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dF9kaXI9IG91dF9kaXIsICAjIGRpciBpcyBhdXRvLWNyZWF0ZWQgZm9yIHN0b3Jpbmcgb3V0cHV0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1GLCAgICMgY2x1c3Rlcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXNpc19tb2RlID0gInN1YmNsdXN0ZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyxkZWJ1Zz1UUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICAgICAgICAgICMgIkdlbm9tZUNvb3JkaW5hdGVzIjogWw0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc4NzMxLTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzg3Mzk2LTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc2NDc5LTExMjM5MzA2OSINCmBgYA0KDQpgYGB7ciBzaG93IENOVl8sIGZpZy5oZWlnaHQ9MzAsIGZpZy53aWR0aD0yNX0NCmltZyA8LSByZWFkUE5HKHBhc3RlKG91dF9kaXIsICIvaW5mZXJjbnYucG5nIiwgc2VwID0gIiIpKQ0KZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCmdyaWQ6OmdyaWQucmFzdGVyKGltZykNCmBgYA0KDQo=